From 24a256ca7e79e5599619e5b149decb28b473745c Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Sat, 11 Sep 2021 00:05:16 +0530 Subject: [PATCH 01/15] Fix `rustdoc` argument error --- compiler/rustc_driver/src/lib.rs | 2 +- src/librustdoc/config.rs | 22 ++++++++++++++++++++-- src/librustdoc/lib.rs | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 563ec8b0e3a70..4f11fbf870cbe 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -930,7 +930,7 @@ fn describe_codegen_flags() { print_flag_list("-C", config::CG_OPTIONS); } -fn print_flag_list( +pub fn print_flag_list( cmdline_opt: &str, flag_list: &[(&'static str, T, &'static str, &'static str)], ) { diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 6e483d27f336b..6997ed7699944 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -6,6 +6,7 @@ use std::path::PathBuf; use std::str::FromStr; use rustc_data_structures::fx::FxHashMap; +use rustc_driver::print_flag_list; use rustc_session::config::{ self, parse_crate_types_from_list, parse_externs, parse_target_triple, CrateType, }; @@ -305,11 +306,12 @@ impl RenderOptions { impl Options { /// Parses the given command-line for options. If an error message or other early-return has /// been printed, returns `Err` with the exit code. - crate fn from_matches(matches: &getopts::Matches) -> Result { + crate fn from_matches(matches: &getopts::Matches, args: Vec) -> Result { + let args = &args[1..]; // Check for unstable options. nightly_options::check_nightly_options(matches, &opts()); - if matches.opt_present("h") || matches.opt_present("help") { + if args.is_empty() || matches.opt_present("h") || matches.opt_present("help") { crate::usage("rustdoc"); return Err(0); } else if matches.opt_present("version") { @@ -330,6 +332,21 @@ impl Options { // check for deprecated options check_deprecated_options(matches, &diag); + let z_flags = matches.opt_strs("Z"); + if z_flags.iter().any(|x| *x == "help") { + print_flag_list("-Z", config::DB_OPTIONS); + return Err(0); + } + let c_flags = matches.opt_strs("C"); + if c_flags.iter().any(|x| *x == "help") { + print_flag_list("-C", config::CG_OPTIONS); + return Err(0); + } + let w_flags = matches.opt_strs("W"); + if w_flags.iter().any(|x| *x == "help") { + print_flag_list("-W", config::DB_OPTIONS); + return Err(0); + } if matches.opt_strs("passes") == ["list"] { println!("Available passes for running rustdoc:"); for pass in passes::PASSES { @@ -410,6 +427,7 @@ impl Options { } return Err(0); } + let (_lint_opts, _describe_lints, _lint_cap) = get_cmd_lint_options(matches, error_format); if matches.free.is_empty() { diag.struct_err("missing file operand").emit(); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 7eff725989cc9..76be107273b3f 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -687,7 +687,7 @@ fn main_args(at_args: &[String]) -> MainResult { // Note that we discard any distinction between different non-zero exit // codes from `from_matches` here. - let options = match config::Options::from_matches(&matches) { + let options = match config::Options::from_matches(&matches, args) { Ok(opts) => opts, Err(code) => return if code == 0 { Ok(()) } else { Err(ErrorReported) }, }; From c56b81e6482f6fedfcb27be156e66399b323c399 Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Wed, 15 Sep 2021 06:58:59 +0530 Subject: [PATCH 02/15] Add test for default rustdoc run --- .../issue-88756-default-output/Makefile | 4 + .../issue-88756-default-output/README.md | 1 + .../output-default.stdout | 192 ++++++++++++++++++ .../run-make/issue-88756-default-output/x.rs | 1 + 4 files changed, 198 insertions(+) create mode 100644 src/test/run-make/issue-88756-default-output/Makefile create mode 100644 src/test/run-make/issue-88756-default-output/README.md create mode 100644 src/test/run-make/issue-88756-default-output/output-default.stdout create mode 100644 src/test/run-make/issue-88756-default-output/x.rs diff --git a/src/test/run-make/issue-88756-default-output/Makefile b/src/test/run-make/issue-88756-default-output/Makefile new file mode 100644 index 0000000000000..88f18c67087d7 --- /dev/null +++ b/src/test/run-make/issue-88756-default-output/Makefile @@ -0,0 +1,4 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + $(BARE_RUSTDOC) 2>&1 | diff - output-default.stdout diff --git a/src/test/run-make/issue-88756-default-output/README.md b/src/test/run-make/issue-88756-default-output/README.md new file mode 100644 index 0000000000000..8cbfac4f7d2f1 --- /dev/null +++ b/src/test/run-make/issue-88756-default-output/README.md @@ -0,0 +1 @@ +This is a test to verify that the default behavior of `rustdoc` is printing out help output instead of erroring out (#88756). diff --git a/src/test/run-make/issue-88756-default-output/output-default.stdout b/src/test/run-make/issue-88756-default-output/output-default.stdout new file mode 100644 index 0000000000000..ed7a057014507 --- /dev/null +++ b/src/test/run-make/issue-88756-default-output/output-default.stdout @@ -0,0 +1,192 @@ +rustdoc [options] + +Options: + -h, --help show this help message + -V, --version print rustdoc's version + -v, --verbose use verbose output + -w, --output-format [html] + the output type to write + --output PATH Which directory to place the output. This option is + deprecated, use --out-dir instead. + -o, --out-dir PATH which directory to place the output + --crate-name NAME + specify the name of this crate + --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro] + Comma separated list of types of crates + for the compiler to emit + -L, --library-path DIR + directory to add to crate search path + --cfg pass a --cfg to rustc + --extern NAME[=PATH] + pass an --extern to rustc + --extern-html-root-url NAME=URL + base URL to use for dependencies; for example, + "std=/doc" links std::vec::Vec to + /doc/std/vec/struct.Vec.html + --extern-html-root-takes-precedence + give precedence to `--extern-html-root-url`, not + `html_root_url` + -C, --codegen OPT[=VALUE] + pass a codegen option to rustc + --document-private-items + document private items + --document-hidden-items + document items that have doc(hidden) + --test run code examples as tests + --test-args ARGS + arguments to pass to the test runner + --test-run-directory PATH + The working directory in which to run tests + --target TRIPLE target triple to document + --markdown-css FILES + CSS files to include via in a rendered Markdown + file + --html-in-header FILES + files to include inline in the section of a + rendered Markdown file or generated documentation + --html-before-content FILES + files to include inline between and the content + of a rendered Markdown file or generated documentation + --html-after-content FILES + files to include inline between the content and + of a rendered Markdown file or generated + documentation + --markdown-before-content FILES + files to include inline between and the content + of a rendered Markdown file or generated documentation + --markdown-after-content FILES + files to include inline between the content and + of a rendered Markdown file or generated + documentation + --markdown-playground-url URL + URL to send code snippets to + --markdown-no-toc + don't include table of contents + -e, --extend-css PATH + To add some CSS rules with a given file to generate + doc with your own theme. However, your theme might + break if the rustdoc's generated HTML changes, so be + careful! + -Z FLAG internal and debugging options (only on nightly build) + --sysroot PATH Override the system root + --playground-url URL + URL to send code snippets to, may be reset by + --markdown-playground-url or + `#![doc(html_playground_url=...)]` + --display-doctest-warnings + show warnings that originate in doctests + --crate-version VERSION + crate version to print into documentation + --sort-modules-by-appearance + sort modules by where they appear in the program, + rather than alphabetically + --default-theme THEME + Set the default theme. THEME should be the theme name, + generally lowercase. If an unknown default theme is + specified, the builtin default is used. The set of + themes, and the rustdoc built-in default, are not + stable. + --default-setting SETTING[=VALUE] + Default value for a rustdoc setting (used when + "rustdoc-SETTING" is absent from web browser Local + Storage). If VALUE is not supplied, "true" is used. + Supported SETTINGs and VALUEs are not documented and + not stable. + --theme FILES additional themes which will be added to the generated + docs + --check-theme FILES + check if given theme is valid + --resource-suffix PATH + suffix to add to CSS and JavaScript files, e.g., + "light.css" will become "light-suffix.css" + --edition EDITION + edition to use when compiling rust code (default: + 2015) + --color auto|always|never + Configure coloring of output: + auto = colorize, if output goes to a tty (default); + always = always colorize output; + never = never colorize output + --error-format human|json|short + How errors and other messages are produced + --json CONFIG Configure the structure of JSON diagnostics + --disable-minification + Disable minification applied on JS files + -A, --allow LINT Set lint allowed + -W, --warn LINT Set lint warnings + --force-warn LINT + Set lint force-warn + -D, --deny LINT Set lint denied + -F, --forbid LINT Set lint forbidden + --cap-lints LEVEL + Set the most restrictive lint level. More restrictive + lints are capped at this level. By default, it is at + `forbid` level. + --index-page PATH + Markdown file to be used as index page + --enable-index-page + To enable generation of the index page + --static-root-path PATH + Path string to force loading static files from in + output pages. If not set, uses combinations of '../' + to reach the documentation root. + --disable-per-crate-search + disables generating the crate selector on the search + box + --persist-doctests PATH + Directory to persist doctest executables into + --show-coverage + calculate percentage of public items with + documentation + --enable-per-target-ignores + parse ignore-foo for ignoring doctests on a per-target + basis + --runtool The tool to run tests with when building for a different target than host + + --runtool-arg One (of possibly many) arguments to pass to the runtool + + --test-builder PATH + The rustc-like binary to use as the test builder + --check Run rustdoc checks + --generate-redirect-map + Generate JSON file at the top level instead of + generating HTML redirection files + --emit [unversioned-shared-resources,toolchain-shared-resources,invocation-specific] + Comma separated list of types of output for rustdoc to + emit + --no-run Compile doctests without running them + --show-type-layout + Include the memory layout of types in the docs + --nocapture Don't capture stdout and stderr of tests + --generate-link-to-definition + Make the identifiers in the HTML source code pages + navigable + --scrape-examples-output-path collect function call information and output at the given path + + --scrape-examples-target-crate collect function call information for functions from the target crate + + --scrape-tests Include test code when scraping examples + --with-examples path to function call information (for displaying examples in the documentation) + + --plugin-path DIR + removed, see issue #44136 + for + more information + --passes PASSES removed, see issue #44136 + for + more information + --plugins PLUGINS + removed, see issue #44136 + for + more information + --no-defaults removed, see issue #44136 + for + more information + -r, --input-format [rust] + removed, see issue #44136 + for + more information + + @path Read newline separated options from `path` + +More information available at https://doc.rust-lang.org/nightly/rustdoc/what-is-rustdoc.html diff --git a/src/test/run-make/issue-88756-default-output/x.rs b/src/test/run-make/issue-88756-default-output/x.rs new file mode 100644 index 0000000000000..5df7576133a68 --- /dev/null +++ b/src/test/run-make/issue-88756-default-output/x.rs @@ -0,0 +1 @@ +// nothing to see here From 873b6ac8f8714f29baee3117d4ed50a39956f829 Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Thu, 27 Jan 2022 13:47:10 +0530 Subject: [PATCH 03/15] Add test for help output with -W --- .../run-make/issue-88756-opt-help/Makefile | 4 + .../run-make/issue-88756-opt-help/README.md | 1 + .../output-default.stdout | 185 ++++++++++++++++++ src/test/run-make/issue-88756-opt-help/x.rs | 1 + 4 files changed, 191 insertions(+) create mode 100644 src/test/run-make/issue-88756-opt-help/Makefile create mode 100644 src/test/run-make/issue-88756-opt-help/README.md create mode 100644 src/test/run-make/issue-88756-opt-help/output-default.stdout create mode 100644 src/test/run-make/issue-88756-opt-help/x.rs diff --git a/src/test/run-make/issue-88756-opt-help/Makefile b/src/test/run-make/issue-88756-opt-help/Makefile new file mode 100644 index 0000000000000..8ababbf5b4ebd --- /dev/null +++ b/src/test/run-make/issue-88756-opt-help/Makefile @@ -0,0 +1,4 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTDOC) -W help 2>&1 | diff - output-default.stdout diff --git a/src/test/run-make/issue-88756-opt-help/README.md b/src/test/run-make/issue-88756-opt-help/README.md new file mode 100644 index 0000000000000..9b742753f25b6 --- /dev/null +++ b/src/test/run-make/issue-88756-opt-help/README.md @@ -0,0 +1 @@ +This is a test to verify that `rustdoc` behaves the same as rustc and prints out help output for its options like -W (#88756). diff --git a/src/test/run-make/issue-88756-opt-help/output-default.stdout b/src/test/run-make/issue-88756-opt-help/output-default.stdout new file mode 100644 index 0000000000000..d32cfad0c90ff --- /dev/null +++ b/src/test/run-make/issue-88756-opt-help/output-default.stdout @@ -0,0 +1,185 @@ + -W allow-features=val -- only allow the listed language features to be enabled in code (space separated) + -W always-encode-mir=val -- encode MIR of all functions into the crate metadata (default: no) + -W assume-incomplete-release=val -- make cfg(version) treat the current version as incomplete (default: no) + -W asm-comments=val -- generate comments into the assembly (may change behavior) (default: no) + -W assert-incr-state=val -- assert that the incremental cache is in given state: either `loaded` or `not-loaded`. + -W ast-json=val -- print the AST as JSON and halt (default: no) + -W ast-json-noexpand=val -- print the pre-expansion AST as JSON and halt (default: no) + -W binary-dep-depinfo=val -- include artifacts (sysroot, crate dependencies) used during compilation in dep-info (default: no) + -W borrowck=val -- select which borrowck is used (`mir` or `migrate`) (default: `migrate`) + -W branch-protection=val -- set options for branch target identification and pointer authentication on AArch64 + -W cf-protection=val -- instrument control-flow architecture protection + -W cgu-partitioning-strategy=val -- the codegen unit partitioning strategy to use + -W chalk=val -- enable the experimental Chalk-based trait solving engine + -W codegen-backend=val -- the backend to use + -W combine-cgu=val -- combine CGUs into a single one + -W crate-attr=val -- inject the given attribute in the crate + -W debug-info-for-profiling=val -- emit discriminators and other data necessary for AutoFDO + -W debug-macros=val -- emit line numbers debug info inside macros (default: no) + -W deduplicate-diagnostics=val -- deduplicate identical diagnostics (default: yes) + -W dep-info-omit-d-target=val -- in dep-info output, omit targets for tracking dependencies of the dep-info files themselves (default: no) + -W dep-tasks=val -- print tasks that execute and the color their dep node gets (requires debug build) (default: no) + -W dlltool=val -- import library generation tool (windows-gnu only) + -W dont-buffer-diagnostics=val -- emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) (default: no) + -W drop-tracking=val -- enables drop tracking in generators (default: no) + -W dual-proc-macros=val -- load proc macros for both target and host, but only link to the target (default: no) + -W dump-dep-graph=val -- dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) (default: no) + -W dump-mir=val -- dump MIR state to file. + `val` is used to select which passes and functions to dump. For example: + `all` matches all passes and functions, + `foo` matches all passes for functions whose name contains 'foo', + `foo & ConstProp` only the 'ConstProp' pass for function names containing 'foo', + `foo | bar` all passes for function names containing 'foo' or 'bar'. + -W dump-mir-dataflow=val -- in addition to `.mir` files, create graphviz `.dot` files with dataflow results (default: no) + -W dump-mir-dir=val -- the directory the MIR is dumped into (default: `mir_dump`) + -W dump-mir-exclude-pass-number=val -- exclude the pass number when dumping MIR (used in tests) (default: no) + -W dump-mir-graphviz=val -- in addition to `.mir` files, create graphviz `.dot` files (and with `-Z instrument-coverage`, also create a `.dot` file for the MIR-derived coverage graph) (default: no) + -W dump-mir-spanview=val -- in addition to `.mir` files, create `.html` files to view spans for all `statement`s (including terminators), only `terminator` spans, or computed `block` spans (one span encompassing a block's terminator and all statements). If `-Z instrument-coverage` is also enabled, create an additional `.html` file showing the computed coverage spans. + -W emit-stack-sizes=val -- emit a section containing stack size metadata (default: no) + -W fewer-names=val -- reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) (default: no) + -W force-unstable-if-unmarked=val -- force all crates to be `rustc_private` unstable (default: no) + -W fuel=val -- set the optimization fuel quota for a crate + -W function-sections=val -- whether each function should go in its own section + -W future-incompat-test=val -- forces all lints to be future incompatible, used for internal testing (default: no) + -W gcc-ld=val -- implementation of ld used by cc + -W graphviz-dark-mode=val -- use dark-themed colors in graphviz output (default: no) + -W graphviz-font=val -- use the given `fontname` in graphviz output; can be overridden by setting environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`) + -W hir-stats=val -- print some statistics about AST and HIR (default: no) + -W human-readable-cgu-names=val -- generate human-readable, predictable names for codegen units (default: no) + -W identify-regions=val -- display unnamed regions as `'`, using a non-ident unique id (default: no) + -W incremental-ignore-spans=val -- ignore spans during ICH computation -- used for testing (default: no) + -W incremental-info=val -- print high-level information about incremental reuse (or the lack thereof) (default: no) + -W incremental-relative-spans=val -- hash spans relative to their parent item for incr. comp. (default: no) + -W incremental-verify-ich=val -- verify incr. comp. hashes of green query instances (default: no) + -W inline-mir=val -- enable MIR inlining (default: no) + -W inline-mir-threshold=val -- a default MIR inlining threshold (default: 50) + -W inline-mir-hint-threshold=val -- inlining threshold for functions with inline hint (default: 100) + -W inline-in-all-cgus=val -- control whether `#[inline]` functions are in all CGUs + -W input-stats=val -- gather statistics about the input (default: no) + -W instrument-coverage=val -- instrument the generated code to support LLVM source-based code coverage reports (note, the compiler build config must include `profiler = true`); implies `-C symbol-mangling-version=v0`. Optional values are: + `=all` (implicit value) + `=except-unused-generics` + `=except-unused-functions` + `=off` (default) + -W instrument-mcount=val -- insert function instrument code for mcount-based tracing (default: no) + -W keep-hygiene-data=val -- keep hygiene data after analysis (default: no) + -W link-native-libraries=val -- link native libraries in the linker invocation (default: yes) + -W link-only=val -- link the `.rlink` file generated by `-Z no-link` (default: no) + -W llvm-plugins=val -- a list LLVM plugins to enable (space separated) + -W llvm-time-trace=val -- generate JSON tracing data file from LLVM data (default: no) + -W location-detail=val -- comma seperated list of location details to be tracked when using caller_location valid options are `file`, `line`, and `column` (default: all) + -W ls=val -- list the symbols defined by a library crate (default: no) + -W macro-backtrace=val -- show macro backtraces (default: no) + -W merge-functions=val -- control the operation of the MergeFunctions LLVM pass, taking the same values as the target option of the same name + -W meta-stats=val -- gather metadata statistics (default: no) + -W mir-emit-retag=val -- emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 (default: no) + -W mir-opt-level=val -- MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds) + -W move-size-limit=val -- the size at which the `large_assignments` lint starts to be emitted + -W mutable-noalias=val -- emit noalias metadata for mutable references (default: yes) + -W new-llvm-pass-manager=val -- use new LLVM pass manager (default: no) + -W nll-facts=val -- dump facts from NLL analysis into side files (default: no) + -W nll-facts-dir=val -- the directory the NLL facts are dumped into (default: `nll-facts`) + -W no-analysis=val -- parse and expand the source, but run no analysis + -W no-codegen=val -- run all passes except codegen; no output + -W no-generate-arange-section=val -- omit DWARF address ranges that give faster lookups + -W no-interleave-lints=val -- execute lints separately; allows benchmarking individual lints + -W no-leak-check=val -- disable the 'leak check' for subtyping; unsound, but useful for tests + -W no-link=val -- compile without linking + -W no-parallel-llvm=val -- run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO) + -W no-unique-section-names=val -- do not use unique names for text and data sections when -Z function-sections is used + -W no-profiler-runtime=val -- prevent automatic injection of the profiler_builtins crate + -W normalize-docs=val -- normalize associated items in rustdoc when generating documentation + -W osx-rpath-install-name=val -- pass `-install_name @rpath/...` to the macOS linker (default: no) + -W panic-abort-tests=val -- support compiling tests with panic=abort (default: no) + -W panic-in-drop=val -- panic strategy for panics in drops + -W parse-only=val -- parse only; do not compile, assemble, or link (default: no) + -W partially-uninit-const-threshold=val -- allow generating const initializers with mixed init/uninit bytes, and set the maximum total size of a const allocation for which this is allowed (default: never) + -W perf-stats=val -- print some performance-related statistics (default: no) + -W pick-stable-methods-before-any-unstable=val -- try to pick stable methods first before picking any unstable methods (default: yes) + -W plt=val -- whether to use the PLT when calling into shared libraries; + only has effect for PIC code on systems with ELF binaries + (default: PLT is disabled if full relro is enabled) + -W polonius=val -- enable polonius-based borrow-checker (default: no) + -W polymorphize=val -- perform polymorphization analysis + -W pre-link-arg=val -- a single extra argument to prepend the linker invocation (can be used several times) + -W pre-link-args=val -- extra arguments to prepend to the linker invocation (space separated) + -W precise-enum-drop-elaboration=val -- use a more precise version of drop elaboration for matches on enums (default: yes). This results in better codegen, but has caused miscompilations on some tier 2 platforms. See #77382 and #74551. + -W print-fuel=val -- make rustc print the total optimization fuel used by a crate + -W print-llvm-passes=val -- print the LLVM optimization passes being run (default: no) + -W print-mono-items=val -- print the result of the monomorphization collection pass + -W print-type-sizes=val -- print layout information for each type encountered (default: no) + -W proc-macro-backtrace=val -- show backtraces for panics during proc-macro execution (default: no) + -W profile=val -- insert profiling code (default: no) + -W profile-closures=val -- profile size of closures + -W profile-emit=val -- file path to emit profiling data at runtime when using 'profile' (default based on relative source path) + -W profiler-runtime=val -- name of the profiler runtime crate to automatically inject (default: `profiler_builtins`) + -W profile-sample-use=val -- use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO) + -W query-dep-graph=val -- enable queries of the dependency graph for regression testing (default: no) + -W randomize-layout=val -- randomize the layout of types (default: no) + -W layout-seed=val -- seed layout randomization + -W relax-elf-relocations=val -- whether ELF relocations can be relaxed + -W relro-level=val -- choose which RELRO level to use + -W remap-cwd-prefix=val -- remap paths under the current working directory to this path prefix + -W simulate-remapped-rust-src-base=val -- simulate the effect of remap-debuginfo = true at bootstrapping by remapping path to rust's source base directory. only meant for testing purposes + -W report-delayed-bugs=val -- immediately print bugs registered with `delay_span_bug` (default: no) + -W sanitizer=val -- use a sanitizer + -W sanitizer-memory-track-origins=val -- enable origins tracking in MemorySanitizer + -W sanitizer-recover=val -- enable recovery for selected sanitizers + -W saturating-float-casts=val -- make float->int casts UB-free: numbers outside the integer type's range are clipped to the max/min integer respectively, and NaN is mapped to 0 (default: yes) + -W save-analysis=val -- write syntax and type analysis (in JSON format) information, in addition to normal output (default: no) + -W self-profile=val -- run the self profiler and output the raw event data + -W self-profile-events=val -- specify the events recorded by the self profiler; + for example: `-Z self-profile-events=default,query-keys` + all options: none, all, default, generic-activity, query-provider, query-cache-hit + query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes + -W share-generics=val -- make the current crate share its generic instantiations + -W show-span=val -- show spans for compiler debugging (expr|pat|ty) + -W span-debug=val -- forward proc_macro::Span's `Debug` impl to `Span` + -W span-free-formats=val -- exclude spans when debug-printing compiler state (default: no) + -W src-hash-algorithm=val -- hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`) + -W stack-protector=val -- control stack smash protection strategy (`rustc --print stack-protector-strategies` for details) + -W strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`) + -W split-dwarf-kind=val -- split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) + (default: `split`) + + `split`: sections which do not require relocation are written into a DWARF object (`.dwo`) + file which is ignored by the linker + `single`: sections which do not require relocation are written into object file but ignored + by the linker + -W split-dwarf-inlining=val -- provide minimal debug info in the object/executable to facilitate online symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF + -W symbol-mangling-version=val -- which mangling version to use for symbol names ('legacy' (default) or 'v0') + -W teach=val -- show extended diagnostic help (default: no) + -W temps-dir=val -- the directory the intermediate files are written to + -W terminal-width=val -- set the current terminal width + -W tune-cpu=val -- select processor to schedule for (`rustc --print target-cpus` for details) + -W thinlto=val -- enable ThinLTO when possible + -W thir-unsafeck=val -- use the THIR unsafety checker (default: no) + -W threads=val -- use a thread pool with N threads + -W time=val -- measure time of rustc processes (default: no) + -W time-llvm-passes=val -- measure time of each LLVM pass (default: no) + -W time-passes=val -- measure time of each rustc pass (default: no) + -W tls-model=val -- choose the TLS model to use (`rustc --print tls-models` for details) + -W trace-macros=val -- for every macro invocation, print its name and arguments (default: no) + -W trap-unreachable=val -- generate trap instructions for unreachable intrinsics (default: use target setting, usually yes) + -W treat-err-as-bug=val -- treat error number `val` that occurs as bug + -W trim-diagnostic-paths=val -- in diagnostics, use heuristics to shorten paths referring to items + -W ui-testing=val -- emit compiler diagnostics in a form suitable for UI testing (default: no) + -W unleash-the-miri-inside-of-you=val -- take the brakes off const evaluation. NOTE: this is unsound (default: no) + -W unpretty=val -- present the input source, unstable (and less-pretty) variants; + `normal`, `identified`, + `expanded`, `expanded,identified`, + `expanded,hygiene` (with internal representations), + `everybody_loops` (all function bodies replaced with `loop {}`), + `ast-tree` (raw AST before expansion), + `ast-tree,expanded` (raw AST after expansion), + `hir` (the HIR), `hir,identified`, + `hir,typed` (HIR with types for each node), + `hir-tree` (dump the raw HIR), + `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) + -W unsound-mir-opts=val -- enable unsound and buggy MIR optimizations (default: no) + -W unstable-options=val -- adds unstable command line options to rustc interface (default: no) + -W use-ctors-section=val -- use legacy .ctors section for initializers rather than .init_array + -W validate-mir=val -- validate MIR after each transformation + -W verbose=val -- in general, enable more debug printouts (default: no) + -W verify-llvm-ir=val -- verify LLVM IR (default: no) + -W wasi-exec-model=val -- whether to build a wasi command or reactor diff --git a/src/test/run-make/issue-88756-opt-help/x.rs b/src/test/run-make/issue-88756-opt-help/x.rs new file mode 100644 index 0000000000000..5df7576133a68 --- /dev/null +++ b/src/test/run-make/issue-88756-opt-help/x.rs @@ -0,0 +1 @@ +// nothing to see here From e53e7906105a7eee51a38763503a4bf9d9a628af Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Mon, 21 Feb 2022 13:38:49 +0530 Subject: [PATCH 04/15] bless test for issue-83883 --- .../issue-83883-describe-lints.stdout | 208 ++++++++++++++++-- 1 file changed, 184 insertions(+), 24 deletions(-) diff --git a/src/test/rustdoc-ui/issue-83883-describe-lints.stdout b/src/test/rustdoc-ui/issue-83883-describe-lints.stdout index 651faf5761f18..d32cfad0c90ff 100644 --- a/src/test/rustdoc-ui/issue-83883-describe-lints.stdout +++ b/src/test/rustdoc-ui/issue-83883-describe-lints.stdout @@ -1,25 +1,185 @@ + -W allow-features=val -- only allow the listed language features to be enabled in code (space separated) + -W always-encode-mir=val -- encode MIR of all functions into the crate metadata (default: no) + -W assume-incomplete-release=val -- make cfg(version) treat the current version as incomplete (default: no) + -W asm-comments=val -- generate comments into the assembly (may change behavior) (default: no) + -W assert-incr-state=val -- assert that the incremental cache is in given state: either `loaded` or `not-loaded`. + -W ast-json=val -- print the AST as JSON and halt (default: no) + -W ast-json-noexpand=val -- print the pre-expansion AST as JSON and halt (default: no) + -W binary-dep-depinfo=val -- include artifacts (sysroot, crate dependencies) used during compilation in dep-info (default: no) + -W borrowck=val -- select which borrowck is used (`mir` or `migrate`) (default: `migrate`) + -W branch-protection=val -- set options for branch target identification and pointer authentication on AArch64 + -W cf-protection=val -- instrument control-flow architecture protection + -W cgu-partitioning-strategy=val -- the codegen unit partitioning strategy to use + -W chalk=val -- enable the experimental Chalk-based trait solving engine + -W codegen-backend=val -- the backend to use + -W combine-cgu=val -- combine CGUs into a single one + -W crate-attr=val -- inject the given attribute in the crate + -W debug-info-for-profiling=val -- emit discriminators and other data necessary for AutoFDO + -W debug-macros=val -- emit line numbers debug info inside macros (default: no) + -W deduplicate-diagnostics=val -- deduplicate identical diagnostics (default: yes) + -W dep-info-omit-d-target=val -- in dep-info output, omit targets for tracking dependencies of the dep-info files themselves (default: no) + -W dep-tasks=val -- print tasks that execute and the color their dep node gets (requires debug build) (default: no) + -W dlltool=val -- import library generation tool (windows-gnu only) + -W dont-buffer-diagnostics=val -- emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) (default: no) + -W drop-tracking=val -- enables drop tracking in generators (default: no) + -W dual-proc-macros=val -- load proc macros for both target and host, but only link to the target (default: no) + -W dump-dep-graph=val -- dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) (default: no) + -W dump-mir=val -- dump MIR state to file. + `val` is used to select which passes and functions to dump. For example: + `all` matches all passes and functions, + `foo` matches all passes for functions whose name contains 'foo', + `foo & ConstProp` only the 'ConstProp' pass for function names containing 'foo', + `foo | bar` all passes for function names containing 'foo' or 'bar'. + -W dump-mir-dataflow=val -- in addition to `.mir` files, create graphviz `.dot` files with dataflow results (default: no) + -W dump-mir-dir=val -- the directory the MIR is dumped into (default: `mir_dump`) + -W dump-mir-exclude-pass-number=val -- exclude the pass number when dumping MIR (used in tests) (default: no) + -W dump-mir-graphviz=val -- in addition to `.mir` files, create graphviz `.dot` files (and with `-Z instrument-coverage`, also create a `.dot` file for the MIR-derived coverage graph) (default: no) + -W dump-mir-spanview=val -- in addition to `.mir` files, create `.html` files to view spans for all `statement`s (including terminators), only `terminator` spans, or computed `block` spans (one span encompassing a block's terminator and all statements). If `-Z instrument-coverage` is also enabled, create an additional `.html` file showing the computed coverage spans. + -W emit-stack-sizes=val -- emit a section containing stack size metadata (default: no) + -W fewer-names=val -- reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) (default: no) + -W force-unstable-if-unmarked=val -- force all crates to be `rustc_private` unstable (default: no) + -W fuel=val -- set the optimization fuel quota for a crate + -W function-sections=val -- whether each function should go in its own section + -W future-incompat-test=val -- forces all lints to be future incompatible, used for internal testing (default: no) + -W gcc-ld=val -- implementation of ld used by cc + -W graphviz-dark-mode=val -- use dark-themed colors in graphviz output (default: no) + -W graphviz-font=val -- use the given `fontname` in graphviz output; can be overridden by setting environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`) + -W hir-stats=val -- print some statistics about AST and HIR (default: no) + -W human-readable-cgu-names=val -- generate human-readable, predictable names for codegen units (default: no) + -W identify-regions=val -- display unnamed regions as `'`, using a non-ident unique id (default: no) + -W incremental-ignore-spans=val -- ignore spans during ICH computation -- used for testing (default: no) + -W incremental-info=val -- print high-level information about incremental reuse (or the lack thereof) (default: no) + -W incremental-relative-spans=val -- hash spans relative to their parent item for incr. comp. (default: no) + -W incremental-verify-ich=val -- verify incr. comp. hashes of green query instances (default: no) + -W inline-mir=val -- enable MIR inlining (default: no) + -W inline-mir-threshold=val -- a default MIR inlining threshold (default: 50) + -W inline-mir-hint-threshold=val -- inlining threshold for functions with inline hint (default: 100) + -W inline-in-all-cgus=val -- control whether `#[inline]` functions are in all CGUs + -W input-stats=val -- gather statistics about the input (default: no) + -W instrument-coverage=val -- instrument the generated code to support LLVM source-based code coverage reports (note, the compiler build config must include `profiler = true`); implies `-C symbol-mangling-version=v0`. Optional values are: + `=all` (implicit value) + `=except-unused-generics` + `=except-unused-functions` + `=off` (default) + -W instrument-mcount=val -- insert function instrument code for mcount-based tracing (default: no) + -W keep-hygiene-data=val -- keep hygiene data after analysis (default: no) + -W link-native-libraries=val -- link native libraries in the linker invocation (default: yes) + -W link-only=val -- link the `.rlink` file generated by `-Z no-link` (default: no) + -W llvm-plugins=val -- a list LLVM plugins to enable (space separated) + -W llvm-time-trace=val -- generate JSON tracing data file from LLVM data (default: no) + -W location-detail=val -- comma seperated list of location details to be tracked when using caller_location valid options are `file`, `line`, and `column` (default: all) + -W ls=val -- list the symbols defined by a library crate (default: no) + -W macro-backtrace=val -- show macro backtraces (default: no) + -W merge-functions=val -- control the operation of the MergeFunctions LLVM pass, taking the same values as the target option of the same name + -W meta-stats=val -- gather metadata statistics (default: no) + -W mir-emit-retag=val -- emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 (default: no) + -W mir-opt-level=val -- MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds) + -W move-size-limit=val -- the size at which the `large_assignments` lint starts to be emitted + -W mutable-noalias=val -- emit noalias metadata for mutable references (default: yes) + -W new-llvm-pass-manager=val -- use new LLVM pass manager (default: no) + -W nll-facts=val -- dump facts from NLL analysis into side files (default: no) + -W nll-facts-dir=val -- the directory the NLL facts are dumped into (default: `nll-facts`) + -W no-analysis=val -- parse and expand the source, but run no analysis + -W no-codegen=val -- run all passes except codegen; no output + -W no-generate-arange-section=val -- omit DWARF address ranges that give faster lookups + -W no-interleave-lints=val -- execute lints separately; allows benchmarking individual lints + -W no-leak-check=val -- disable the 'leak check' for subtyping; unsound, but useful for tests + -W no-link=val -- compile without linking + -W no-parallel-llvm=val -- run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO) + -W no-unique-section-names=val -- do not use unique names for text and data sections when -Z function-sections is used + -W no-profiler-runtime=val -- prevent automatic injection of the profiler_builtins crate + -W normalize-docs=val -- normalize associated items in rustdoc when generating documentation + -W osx-rpath-install-name=val -- pass `-install_name @rpath/...` to the macOS linker (default: no) + -W panic-abort-tests=val -- support compiling tests with panic=abort (default: no) + -W panic-in-drop=val -- panic strategy for panics in drops + -W parse-only=val -- parse only; do not compile, assemble, or link (default: no) + -W partially-uninit-const-threshold=val -- allow generating const initializers with mixed init/uninit bytes, and set the maximum total size of a const allocation for which this is allowed (default: never) + -W perf-stats=val -- print some performance-related statistics (default: no) + -W pick-stable-methods-before-any-unstable=val -- try to pick stable methods first before picking any unstable methods (default: yes) + -W plt=val -- whether to use the PLT when calling into shared libraries; + only has effect for PIC code on systems with ELF binaries + (default: PLT is disabled if full relro is enabled) + -W polonius=val -- enable polonius-based borrow-checker (default: no) + -W polymorphize=val -- perform polymorphization analysis + -W pre-link-arg=val -- a single extra argument to prepend the linker invocation (can be used several times) + -W pre-link-args=val -- extra arguments to prepend to the linker invocation (space separated) + -W precise-enum-drop-elaboration=val -- use a more precise version of drop elaboration for matches on enums (default: yes). This results in better codegen, but has caused miscompilations on some tier 2 platforms. See #77382 and #74551. + -W print-fuel=val -- make rustc print the total optimization fuel used by a crate + -W print-llvm-passes=val -- print the LLVM optimization passes being run (default: no) + -W print-mono-items=val -- print the result of the monomorphization collection pass + -W print-type-sizes=val -- print layout information for each type encountered (default: no) + -W proc-macro-backtrace=val -- show backtraces for panics during proc-macro execution (default: no) + -W profile=val -- insert profiling code (default: no) + -W profile-closures=val -- profile size of closures + -W profile-emit=val -- file path to emit profiling data at runtime when using 'profile' (default based on relative source path) + -W profiler-runtime=val -- name of the profiler runtime crate to automatically inject (default: `profiler_builtins`) + -W profile-sample-use=val -- use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO) + -W query-dep-graph=val -- enable queries of the dependency graph for regression testing (default: no) + -W randomize-layout=val -- randomize the layout of types (default: no) + -W layout-seed=val -- seed layout randomization + -W relax-elf-relocations=val -- whether ELF relocations can be relaxed + -W relro-level=val -- choose which RELRO level to use + -W remap-cwd-prefix=val -- remap paths under the current working directory to this path prefix + -W simulate-remapped-rust-src-base=val -- simulate the effect of remap-debuginfo = true at bootstrapping by remapping path to rust's source base directory. only meant for testing purposes + -W report-delayed-bugs=val -- immediately print bugs registered with `delay_span_bug` (default: no) + -W sanitizer=val -- use a sanitizer + -W sanitizer-memory-track-origins=val -- enable origins tracking in MemorySanitizer + -W sanitizer-recover=val -- enable recovery for selected sanitizers + -W saturating-float-casts=val -- make float->int casts UB-free: numbers outside the integer type's range are clipped to the max/min integer respectively, and NaN is mapped to 0 (default: yes) + -W save-analysis=val -- write syntax and type analysis (in JSON format) information, in addition to normal output (default: no) + -W self-profile=val -- run the self profiler and output the raw event data + -W self-profile-events=val -- specify the events recorded by the self profiler; + for example: `-Z self-profile-events=default,query-keys` + all options: none, all, default, generic-activity, query-provider, query-cache-hit + query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes + -W share-generics=val -- make the current crate share its generic instantiations + -W show-span=val -- show spans for compiler debugging (expr|pat|ty) + -W span-debug=val -- forward proc_macro::Span's `Debug` impl to `Span` + -W span-free-formats=val -- exclude spans when debug-printing compiler state (default: no) + -W src-hash-algorithm=val -- hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`) + -W stack-protector=val -- control stack smash protection strategy (`rustc --print stack-protector-strategies` for details) + -W strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`) + -W split-dwarf-kind=val -- split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) + (default: `split`) -Available lint options: - -W Warn about - -A Allow - -D Deny - -F Forbid (deny and all attempts to override) - - -Lint checks provided by rustc: - - $NAMES $LEVELS $MEANINGS - -Lint groups provided by rustc: - - $NAMES $SUB_LINTS - -Lint checks provided by plugins loaded by this crate: - - $NAMES $LEVELS $MEANINGS - -Lint groups provided by plugins loaded by this crate: - - rustdoc::all $GROUPS - - + `split`: sections which do not require relocation are written into a DWARF object (`.dwo`) + file which is ignored by the linker + `single`: sections which do not require relocation are written into object file but ignored + by the linker + -W split-dwarf-inlining=val -- provide minimal debug info in the object/executable to facilitate online symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF + -W symbol-mangling-version=val -- which mangling version to use for symbol names ('legacy' (default) or 'v0') + -W teach=val -- show extended diagnostic help (default: no) + -W temps-dir=val -- the directory the intermediate files are written to + -W terminal-width=val -- set the current terminal width + -W tune-cpu=val -- select processor to schedule for (`rustc --print target-cpus` for details) + -W thinlto=val -- enable ThinLTO when possible + -W thir-unsafeck=val -- use the THIR unsafety checker (default: no) + -W threads=val -- use a thread pool with N threads + -W time=val -- measure time of rustc processes (default: no) + -W time-llvm-passes=val -- measure time of each LLVM pass (default: no) + -W time-passes=val -- measure time of each rustc pass (default: no) + -W tls-model=val -- choose the TLS model to use (`rustc --print tls-models` for details) + -W trace-macros=val -- for every macro invocation, print its name and arguments (default: no) + -W trap-unreachable=val -- generate trap instructions for unreachable intrinsics (default: use target setting, usually yes) + -W treat-err-as-bug=val -- treat error number `val` that occurs as bug + -W trim-diagnostic-paths=val -- in diagnostics, use heuristics to shorten paths referring to items + -W ui-testing=val -- emit compiler diagnostics in a form suitable for UI testing (default: no) + -W unleash-the-miri-inside-of-you=val -- take the brakes off const evaluation. NOTE: this is unsound (default: no) + -W unpretty=val -- present the input source, unstable (and less-pretty) variants; + `normal`, `identified`, + `expanded`, `expanded,identified`, + `expanded,hygiene` (with internal representations), + `everybody_loops` (all function bodies replaced with `loop {}`), + `ast-tree` (raw AST before expansion), + `ast-tree,expanded` (raw AST after expansion), + `hir` (the HIR), `hir,identified`, + `hir,typed` (HIR with types for each node), + `hir-tree` (dump the raw HIR), + `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) + -W unsound-mir-opts=val -- enable unsound and buggy MIR optimizations (default: no) + -W unstable-options=val -- adds unstable command line options to rustc interface (default: no) + -W use-ctors-section=val -- use legacy .ctors section for initializers rather than .init_array + -W validate-mir=val -- validate MIR after each transformation + -W verbose=val -- in general, enable more debug printouts (default: no) + -W verify-llvm-ir=val -- verify LLVM IR (default: no) + -W wasi-exec-model=val -- whether to build a wasi command or reactor From 47f78a2487010d884bd40af173fddd4ac9d9aba8 Mon Sep 17 00:00:00 2001 From: b-naber Date: Thu, 17 Mar 2022 11:44:57 +0100 Subject: [PATCH 05/15] try to evaluate in try_unify --- compiler/rustc_infer/src/infer/mod.rs | 3 +- compiler/rustc_middle/src/query/mod.rs | 8 +- compiler/rustc_middle/src/ty/relate.rs | 2 +- .../src/traits/const_evaluatable.rs | 88 +++++++++++++------ 4 files changed, 66 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 5697e73f73a70..e1d0da803fce2 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -691,11 +691,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &self, a: ty::Unevaluated<'tcx, ()>, b: ty::Unevaluated<'tcx, ()>, + param_env: ty::ParamEnv<'tcx>, ) -> bool { let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default()); debug!("canonical consts: {:?}", &canonical.value); - self.tcx.try_unify_abstract_consts(canonical.value) + self.tcx.try_unify_abstract_consts(param_env.and(canonical.value)) } pub fn is_in_snapshot(&self) -> bool { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 44b622c1e3d86..d39ae43fe8c1a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -329,12 +329,12 @@ rustc_queries! { } } - query try_unify_abstract_consts(key: ( - ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()> - )) -> bool { + query try_unify_abstract_consts(key: + ty::ParamEnvAnd<'tcx, (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()> + )>) -> bool { desc { |tcx| "trying to unify the generic constants {} and {}", - tcx.def_path_str(key.0.def.did), tcx.def_path_str(key.1.def.did) + tcx.def_path_str(key.value.0.def.did), tcx.def_path_str(key.value.1.def.did) } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 81ee7942c4d34..5d6cbcf690706 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -585,7 +585,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if tcx.features().generic_const_exprs => { - tcx.try_unify_abstract_consts((au.shrink(), bu.shrink())) + tcx.try_unify_abstract_consts(relation.param_env().and((au.shrink(), bu.shrink()))) } // While this is slightly incorrect, it shouldn't matter for `min_const_generics` diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index f880b28b3c8dc..0c33ea858fd0d 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -28,13 +28,13 @@ use std::iter; use std::ops::ControlFlow; /// Check if a given constant can be evaluated. +#[instrument(skip(infcx), level = "debug")] pub fn is_const_evaluatable<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, uv: ty::Unevaluated<'tcx, ()>, param_env: ty::ParamEnv<'tcx>, span: Span, ) -> Result<(), NotConstEvaluatable> { - debug!("is_const_evaluatable({:?})", uv); let tcx = infcx.tcx; if tcx.features().generic_const_exprs { @@ -185,6 +185,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } } +#[instrument(skip(tcx), level = "debug")] fn satisfied_from_param_env<'tcx>( tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, @@ -197,11 +198,12 @@ fn satisfied_from_param_env<'tcx>( // Try to unify with each subtree in the AbstractConst to allow for // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` // predicate for `(N + 1) * 2` - let result = - walk_abstract_const(tcx, b_ct, |b_ct| match try_unify(tcx, ct, b_ct) { + let result = walk_abstract_const(tcx, b_ct, |b_ct| { + match try_unify(tcx, ct, b_ct, param_env) { true => ControlFlow::BREAK, false => ControlFlow::CONTINUE, - }); + } + }); if let ControlFlow::Break(()) = result { debug!("is_const_evaluatable: abstract_const ~~> ok"); @@ -570,11 +572,12 @@ pub(super) fn thir_abstract_const<'tcx>( pub(super) fn try_unify_abstract_consts<'tcx>( tcx: TyCtxt<'tcx>, (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), + param_env: ty::ParamEnv<'tcx>, ) -> bool { (|| { if let Some(a) = AbstractConst::new(tcx, a)? { if let Some(b) = AbstractConst::new(tcx, b)? { - return Ok(try_unify(tcx, a, b)); + return Ok(try_unify(tcx, a, b, param_env)); } } @@ -619,32 +622,59 @@ where recurse(tcx, ct, &mut f) } +// Substitutes generics repeatedly to allow AbstractConsts to unify where a +// ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. +// Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] +#[inline] +#[instrument(skip(tcx), level = "debug")] +fn try_replace_substs_in_root<'tcx>( + tcx: TyCtxt<'tcx>, + mut abstr_const: AbstractConst<'tcx>, +) -> Option> { + while let Node::Leaf(ct) = abstr_const.root(tcx) { + match AbstractConst::from_const(tcx, ct) { + Ok(Some(act)) => abstr_const = act, + Ok(None) => break, + Err(_) => return None, + } + } + + Some(abstr_const) +} + /// Tries to unify two abstract constants using structural equality. +#[instrument(skip(tcx), level = "debug")] pub(super) fn try_unify<'tcx>( tcx: TyCtxt<'tcx>, - mut a: AbstractConst<'tcx>, - mut b: AbstractConst<'tcx>, + a: AbstractConst<'tcx>, + b: AbstractConst<'tcx>, + param_env: ty::ParamEnv<'tcx>, ) -> bool { - // We substitute generics repeatedly to allow AbstractConsts to unify where a - // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. - // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] - while let Node::Leaf(a_ct) = a.root(tcx) { - match AbstractConst::from_const(tcx, a_ct) { - Ok(Some(a_act)) => a = a_act, - Ok(None) => break, - Err(_) => return true, + let a = match try_replace_substs_in_root(tcx, a) { + Some(a) => a, + None => { + return true; } - } - while let Node::Leaf(b_ct) = b.root(tcx) { - match AbstractConst::from_const(tcx, b_ct) { - Ok(Some(b_act)) => b = b_act, - Ok(None) => break, - Err(_) => return true, + }; + + let b = match try_replace_substs_in_root(tcx, b) { + Some(b) => b, + None => { + return true; } - } + }; - match (a.root(tcx), b.root(tcx)) { + let a_root = a.root(tcx); + let b_root = b.root(tcx); + debug!(?a_root, ?b_root); + + match (a_root, b_root) { (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { + let a_ct = a_ct.eval(tcx, param_env); + debug!("a_ct evaluated: {:?}", a_ct); + let b_ct = b_ct.eval(tcx, param_env); + debug!("b_ct evaluated: {:?}", b_ct); + if a_ct.ty() != b_ct.ty() { return false; } @@ -678,23 +708,23 @@ pub(super) fn try_unify<'tcx>( } } (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - try_unify(tcx, a.subtree(al), b.subtree(bl)) - && try_unify(tcx, a.subtree(ar), b.subtree(br)) + try_unify(tcx, a.subtree(al), b.subtree(bl), param_env) + && try_unify(tcx, a.subtree(ar), b.subtree(br), param_env) } (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - try_unify(tcx, a.subtree(av), b.subtree(bv)) + try_unify(tcx, a.subtree(av), b.subtree(bv), param_env) } (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) if a_args.len() == b_args.len() => { - try_unify(tcx, a.subtree(a_f), b.subtree(b_f)) + try_unify(tcx, a.subtree(a_f), b.subtree(b_f), param_env) && iter::zip(a_args, b_args) - .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn))) + .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn), param_env)) } (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) if (a_ty == b_ty) && (a_kind == b_kind) => { - try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand)) + try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand), param_env) } // use this over `_ => false` to make adding variants to `Node` less error prone (Node::Cast(..), _) From ac0458a597674a4afc01a11b97b6b6385727050d Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 21 Mar 2022 18:26:02 +0100 Subject: [PATCH 06/15] stall on on constants that contain infer vars in const_eval_resolve --- compiler/rustc_middle/src/mir/interpret/queries.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 8b0f92d23d75f..155ee210ebe38 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -1,6 +1,7 @@ use super::{ErrorHandled, EvalToConstValueResult, GlobalId}; use crate::mir; +use crate::ty::fold::TypeFoldable; use crate::ty::subst::InternalSubsts; use crate::ty::{self, TyCtxt}; use rustc_hir::def_id::DefId; @@ -38,6 +39,13 @@ impl<'tcx> TyCtxt<'tcx> { ct: ty::Unevaluated<'tcx>, span: Option, ) -> EvalToConstValueResult<'tcx> { + // Cannot resolve `Unevaluated` constants that contain inference + // variables. We reject those here since `resolve_opt_const_arg` + // would fail otherwise + if ct.substs.has_infer_types_or_consts() { + return Err(ErrorHandled::TooGeneric); + } + match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) { Ok(Some(instance)) => { let cid = GlobalId { instance, promoted: ct.promoted }; From 3b9de6b087ffddff6b4e32e070a01799878bc05d Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 21 Mar 2022 18:26:53 +0100 Subject: [PATCH 07/15] dont try to unify unevaluated constants that contain infer vars --- compiler/rustc_infer/src/infer/mod.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index e1d0da803fce2..e7fd7d2928e53 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -687,14 +687,23 @@ pub struct CombinedSnapshot<'a, 'tcx> { impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// calls `tcx.try_unify_abstract_consts` after /// canonicalizing the consts. + #[instrument(skip(self), level = "debug")] pub fn try_unify_abstract_consts( &self, a: ty::Unevaluated<'tcx, ()>, b: ty::Unevaluated<'tcx, ()>, param_env: ty::ParamEnv<'tcx>, ) -> bool { + // Reject any attempt to unify two unevaluated constants that contain inference + // variables. + // FIXME `TyCtxt::const_eval_resolve` already rejects the resolution of those + // constants early, but the canonicalization below messes with that mechanism. + if a.substs.has_infer_types_or_consts() || b.substs.has_infer_types_or_consts() { + debug!("a or b contain infer vars in its substs -> cannot unify"); + return false; + } + let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default()); - debug!("canonical consts: {:?}", &canonical.value); self.tcx.try_unify_abstract_consts(param_env.and(canonical.value)) } @@ -1599,6 +1608,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// /// This handles inferences variables within both `param_env` and `substs` by /// performing the operation on their respective canonical forms. + #[instrument(skip(self), level = "debug")] pub fn const_eval_resolve( &self, param_env: ty::ParamEnv<'tcx>, @@ -1606,15 +1616,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { span: Option, ) -> EvalToConstValueResult<'tcx> { let substs = self.resolve_vars_if_possible(unevaluated.substs); + debug!(?substs); // Postpone the evaluation of constants whose substs depend on inference // variables if substs.has_infer_types_or_consts() { + debug!("has infer types or consts"); return Err(ErrorHandled::TooGeneric); } let param_env_erased = self.tcx.erase_regions(param_env); let substs_erased = self.tcx.erase_regions(substs); + debug!(?param_env_erased); + debug!(?substs_erased); let unevaluated = ty::Unevaluated { def: unevaluated.def, From 6cf3409e16845bf52e72f4ffdac9db139a24a8d3 Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 21 Mar 2022 18:46:20 +0100 Subject: [PATCH 08/15] add/update tests --- .../generic_const_exprs/eval-try-unify.rs | 26 ++++ .../generic_const_exprs/eval-try-unify.stderr | 11 ++ .../ui/const-generics/issues/issue-83765.rs | 13 +- .../const-generics/issues/issue-83765.stderr | 137 ++---------------- 4 files changed, 50 insertions(+), 137 deletions(-) create mode 100644 src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs create mode 100644 src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs new file mode 100644 index 0000000000000..c59d62e576d9b --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs @@ -0,0 +1,26 @@ +// build-pass + +#![feature(generic_const_exprs)] +//~^ WARNING the feature `generic_const_exprs` is incomplete + +trait Generic { + const ASSOC: usize; +} + +impl Generic for u8 { + const ASSOC: usize = 17; +} +impl Generic for u16 { + const ASSOC: usize = 13; +} + + +fn uses_assoc_type() -> [u8; N + T::ASSOC] { + [0; N + T::ASSOC] +} + +fn only_generic_n() -> [u8; N + 13] { + uses_assoc_type::() +} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr new file mode 100644 index 0000000000000..b5719b3fe1dc2 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr @@ -0,0 +1,11 @@ +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/eval-try-unify.rs:3:12 + | +LL | #![feature(generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #76560 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs index 68536348d3884..67d813819e7a3 100644 --- a/src/test/ui/const-generics/issues/issue-83765.rs +++ b/src/test/ui/const-generics/issues/issue-83765.rs @@ -3,6 +3,7 @@ trait TensorDimension { const DIM : usize; + //~^ ERROR cycle detected when resolving instance ` const ISSCALAR : bool = Self::DIM == 0; fn is_scalar(&self) -> bool {Self::ISSCALAR} } @@ -42,22 +43,16 @@ impl<'a,T : Broadcastable,const DIM : usize> TensorDimension for LazyUpdim<'a,T, impl<'a,T : Broadcastable,const DIM : usize> TensorSize for LazyUpdim<'a,T,{T::DIM},DIM> { fn size(&self) -> [usize;DIM] {self.size} - //~^ ERROR method not compatible with trait } impl<'a,T : Broadcastable,const DIM : usize> Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> { type Element = T::Element; fn bget(&self,index:[usize;DIM]) -> Option { - //~^ ERROR method not compatible with trait assert!(DIM >= T::DIM); if !self.inbounds(index) {return None} - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types let size = self.size(); - //~^ ERROR unconstrained generic constant let newindex : [usize;T::DIM] = Default::default(); - //~^ ERROR the trait bound `[usize; _]: Default` is not satisfied self.reference.bget(newindex) } } @@ -76,9 +71,6 @@ impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , const DIM: usize> TensorSize for BMap<'a,R,T,F,DIM> { fn size(&self) -> [usize;DIM] {self.reference.size()} - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types - //~| ERROR method not compatible with trait } impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , @@ -86,10 +78,7 @@ impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , type Element = R; fn bget(&self,index:[usize;DIM]) -> Option { - //~^ ERROR method not compatible with trait self.reference.bget(index).map(&self.closure) - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types } } diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr index a49f850717f8a..0003969f7ac2d 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -1,130 +1,17 @@ -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:44:5 +error[E0391]: cycle detected when resolving instance ` as TensorDimension>::DIM` + --> $DIR/issue-83765.rs:5:5 | -LL | fn size(&self) -> [usize;DIM] {self.size} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` +LL | const DIM : usize; + | ^^^^^^^^^^^^^^^^^^ | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:51:5 - | -LL | fn bget(&self,index:[usize;DIM]) -> Option { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:78:5 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:88:5 - | -LL | fn bget(&self,index:[usize;DIM]) -> Option { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:54:18 - | -LL | if !self.inbounds(index) {return None} - | ^^^^^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::inbounds` - --> $DIR/issue-83765.rs:12:38 - | -LL | fn inbounds(&self,index : [usize;Self::DIM]) -> bool { - | ^^^^^^^^^ required by this bound in `TensorSize::inbounds` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:54:27 - | -LL | if !self.inbounds(index) {return None} - | ^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:57:25 - | -LL | let size = self.size(); - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::size` - --> $DIR/issue-83765.rs:11:30 - | -LL | fn size(&self) -> [usize;Self::DIM]; - | ^^^^^^^^^ required by this bound in `TensorSize::size` - -error[E0277]: the trait bound `[usize; _]: Default` is not satisfied - --> $DIR/issue-83765.rs:59:41 - | -LL | let newindex : [usize;T::DIM] = Default::default(); - | ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[usize; _]` - | -help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement - | -LL | impl<'a,T : Broadcastable,const DIM : usize> Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> where [usize; _]: Default - | +++++++++++++++++++++++++ - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:78:51 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::size` - --> $DIR/issue-83765.rs:11:30 - | -LL | fn size(&self) -> [usize;Self::DIM]; - | ^^^^^^^^^ required by this bound in `TensorSize::size` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:78:36 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM` - | - = note: expected type `DIM` - found type `Self::DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:90:24 - | -LL | self.reference.bget(index).map(&self.closure) - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `Broadcastable::bget` - --> $DIR/issue-83765.rs:20:33 - | -LL | fn bget(&self, index:[usize;Self::DIM]) -> Option; - | ^^^^^^^^^ required by this bound in `Broadcastable::bget` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:90:29 - | -LL | self.reference.bget(index).map(&self.closure) - | ^^^^^ expected `Self::DIM`, found `DIM` +note: ...which requires checking if `TensorDimension` fulfills its obligations... + --> $DIR/issue-83765.rs:4:1 | - = note: expected type `Self::DIM` - found type `DIM` +LL | trait TensorDimension { + | ^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires resolving instance ` as TensorDimension>::DIM`, completing the cycle + = note: cycle used when normalizing ` as TensorDimension>::DIM` -error: aborting due to 12 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0277, E0308. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0391`. From 8ff1edbe5e38cede450d926d2761191f07bb0af1 Mon Sep 17 00:00:00 2001 From: b-naber Date: Tue, 22 Mar 2022 10:38:46 +0100 Subject: [PATCH 09/15] fix previous failures and address review --- compiler/rustc_infer/src/infer/mod.rs | 13 +- .../rustc_middle/src/mir/interpret/queries.rs | 2 +- .../src/traits/const_evaluatable.rs | 208 ++++++++++-------- .../src/traits/fulfill.rs | 6 +- .../rustc_trait_selection/src/traits/mod.rs | 5 +- .../src/traits/select/mod.rs | 6 +- compiler/rustc_typeck/src/check/dropck.rs | 2 +- .../ui/const-generics/issues/issue-83765.rs | 118 +++++----- .../const-generics/issues/issue-83765.stderr | 10 +- 9 files changed, 204 insertions(+), 166 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index e7fd7d2928e53..45502c720ae79 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -20,8 +20,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; -use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::mir::interpret::EvalToConstValueResult; +use rustc_middle::mir::interpret::{ErrorHandled, EvalToConstValueResult}; use rustc_middle::traits::select; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; @@ -695,9 +694,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> bool { // Reject any attempt to unify two unevaluated constants that contain inference - // variables. - // FIXME `TyCtxt::const_eval_resolve` already rejects the resolution of those - // constants early, but the canonicalization below messes with that mechanism. + // variables, since inference variables in queries lead to ICEs. if a.substs.has_infer_types_or_consts() || b.substs.has_infer_types_or_consts() { debug!("a or b contain infer vars in its substs -> cannot unify"); return false; @@ -1621,8 +1618,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // Postpone the evaluation of constants whose substs depend on inference // variables if substs.has_infer_types_or_consts() { - debug!("has infer types or consts"); - return Err(ErrorHandled::TooGeneric); + debug!("substs have infer types or consts: {:?}", substs); + if substs.has_infer_types_or_consts() { + return Err(ErrorHandled::TooGeneric); + } } let param_env_erased = self.tcx.erase_regions(param_env); diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 155ee210ebe38..639a9bc600ac4 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -43,7 +43,7 @@ impl<'tcx> TyCtxt<'tcx> { // variables. We reject those here since `resolve_opt_const_arg` // would fail otherwise if ct.substs.has_infer_types_or_consts() { - return Err(ErrorHandled::TooGeneric); + bug!("did not expect inference variables here"); } match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) { diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 0c33ea858fd0d..c44e961363048 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -569,6 +569,18 @@ pub(super) fn thir_abstract_const<'tcx>( } } +/// Tries to unify two abstract constants using structural equality. +#[instrument(skip(tcx), level = "debug")] +pub(super) fn try_unify<'tcx>( + tcx: TyCtxt<'tcx>, + a: AbstractConst<'tcx>, + b: AbstractConst<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> bool { + let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); + const_unify_ctxt.try_unify_inner(a, b) +} + pub(super) fn try_unify_abstract_consts<'tcx>( tcx: TyCtxt<'tcx>, (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), @@ -622,115 +634,119 @@ where recurse(tcx, ct, &mut f) } -// Substitutes generics repeatedly to allow AbstractConsts to unify where a -// ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. -// Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] -#[inline] -#[instrument(skip(tcx), level = "debug")] -fn try_replace_substs_in_root<'tcx>( +pub(super) struct ConstUnifyCtxt<'tcx> { tcx: TyCtxt<'tcx>, - mut abstr_const: AbstractConst<'tcx>, -) -> Option> { - while let Node::Leaf(ct) = abstr_const.root(tcx) { - match AbstractConst::from_const(tcx, ct) { - Ok(Some(act)) => abstr_const = act, - Ok(None) => break, - Err(_) => return None, - } - } - - Some(abstr_const) + param_env: ty::ParamEnv<'tcx>, } -/// Tries to unify two abstract constants using structural equality. -#[instrument(skip(tcx), level = "debug")] -pub(super) fn try_unify<'tcx>( - tcx: TyCtxt<'tcx>, - a: AbstractConst<'tcx>, - b: AbstractConst<'tcx>, - param_env: ty::ParamEnv<'tcx>, -) -> bool { - let a = match try_replace_substs_in_root(tcx, a) { - Some(a) => a, - None => { - return true; +impl<'tcx> ConstUnifyCtxt<'tcx> { + pub(super) fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { + ConstUnifyCtxt { tcx, param_env } + } + + // Substitutes generics repeatedly to allow AbstractConsts to unify where a + // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. + // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] + #[inline] + #[instrument(skip(self), level = "debug")] + pub(super) fn try_replace_substs_in_root( + &self, + mut abstr_const: AbstractConst<'tcx>, + ) -> Option> { + while let Node::Leaf(ct) = abstr_const.root(self.tcx) { + match AbstractConst::from_const(self.tcx, ct) { + Ok(Some(act)) => abstr_const = act, + Ok(None) => break, + Err(_) => return None, + } } - }; - let b = match try_replace_substs_in_root(tcx, b) { - Some(b) => b, - None => { + Some(abstr_const) + } + + /// Tries to unify two abstract constants using structural equality. + #[instrument(skip(self), level = "debug")] + fn try_unify_inner(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { + let a = if let Some(a) = self.try_replace_substs_in_root(a) { + a + } else { return true; - } - }; + }; - let a_root = a.root(tcx); - let b_root = b.root(tcx); - debug!(?a_root, ?b_root); + let b = if let Some(b) = self.try_replace_substs_in_root(b) { + b + } else { + return true; + }; - match (a_root, b_root) { - (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - let a_ct = a_ct.eval(tcx, param_env); - debug!("a_ct evaluated: {:?}", a_ct); - let b_ct = b_ct.eval(tcx, param_env); - debug!("b_ct evaluated: {:?}", b_ct); + let a_root = a.root(self.tcx); + let b_root = b.root(self.tcx); + debug!(?a_root, ?b_root); - if a_ct.ty() != b_ct.ty() { - return false; - } + match (a_root, b_root) { + (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { + let a_ct = a_ct.eval(self.tcx, self.param_env); + debug!("a_ct evaluated: {:?}", a_ct); + let b_ct = b_ct.eval(self.tcx, self.param_env); + debug!("b_ct evaluated: {:?}", b_ct); - match (a_ct.val(), b_ct.val()) { - // We can just unify errors with everything to reduce the amount of - // emitted errors here. - (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, - (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { - a_param == b_param + if a_ct.ty() != b_ct.ty() { + return false; } - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - // If we have `fn a() -> [u8; N + 1]` and `fn b() -> [u8; 1 + M]` - // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This - // means that we only allow inference variables if they are equal. - (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, - // We expand generic anonymous constants at the start of this function, so this - // branch should only be taking when dealing with associated constants, at - // which point directly comparing them seems like the desired behavior. - // - // FIXME(generic_const_exprs): This isn't actually the case. - // We also take this branch for concrete anonymous constants and - // expand generic anonymous constants with concrete substs. - (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { - a_uv == b_uv + + match (a_ct.val(), b_ct.val()) { + // We can just unify errors with everything to reduce the amount of + // emitted errors here. + (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, + (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { + a_param == b_param + } + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, + // If we have `fn a() -> [u8; N + 1]` and `fn b() -> [u8; 1 + M]` + // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This + // means that we only allow inference variables if they are equal. + (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, + // We expand generic anonymous constants at the start of this function, so this + // branch should only be taking when dealing with associated constants, at + // which point directly comparing them seems like the desired behavior. + // + // FIXME(generic_const_exprs): This isn't actually the case. + // We also take this branch for concrete anonymous constants and + // expand generic anonymous constants with concrete substs. + (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { + a_uv == b_uv + } + // FIXME(generic_const_exprs): We may want to either actually try + // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like + // this, for now we just return false here. + _ => false, } - // FIXME(generic_const_exprs): We may want to either actually try - // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like - // this, for now we just return false here. - _ => false, } + (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { + self.try_unify_inner(a.subtree(al), b.subtree(bl)) + && self.try_unify_inner(a.subtree(ar), b.subtree(br)) + } + (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { + self.try_unify_inner(a.subtree(av), b.subtree(bv)) + } + (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) + if a_args.len() == b_args.len() => + { + self.try_unify_inner(a.subtree(a_f), b.subtree(b_f)) + && iter::zip(a_args, b_args) + .all(|(&an, &bn)| self.try_unify_inner(a.subtree(an), b.subtree(bn))) + } + (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) + if (a_ty == b_ty) && (a_kind == b_kind) => + { + self.try_unify_inner(a.subtree(a_operand), b.subtree(b_operand)) + } + // use this over `_ => false` to make adding variants to `Node` less error prone + (Node::Cast(..), _) + | (Node::FunctionCall(..), _) + | (Node::UnaryOp(..), _) + | (Node::Binop(..), _) + | (Node::Leaf(..), _) => false, } - (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - try_unify(tcx, a.subtree(al), b.subtree(bl), param_env) - && try_unify(tcx, a.subtree(ar), b.subtree(br), param_env) - } - (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - try_unify(tcx, a.subtree(av), b.subtree(bv), param_env) - } - (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) - if a_args.len() == b_args.len() => - { - try_unify(tcx, a.subtree(a_f), b.subtree(b_f), param_env) - && iter::zip(a_args, b_args) - .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn), param_env)) - } - (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) - if (a_ty == b_ty) && (a_kind == b_kind) => - { - try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand), param_env) - } - // use this over `_ => false` to make adding variants to `Node` less error prone - (Node::Cast(..), _) - | (Node::FunctionCall(..), _) - | (Node::UnaryOp(..), _) - | (Node::Binop(..), _) - | (Node::Leaf(..), _) => false, } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 62c6c8454797a..df1e8cdcde00d 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -581,7 +581,11 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = (c1.val(), c2.val()) { - if infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) { + if infcx.try_unify_abstract_consts( + a.shrink(), + b.shrink(), + obligation.param_env, + ) { return ProcessResult::Changed(vec![]); } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index bf94c61e6c2ce..e06ecc528aaa6 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -860,7 +860,10 @@ pub fn provide(providers: &mut ty::query::Providers) { ty::WithOptConstParam { did, const_param_did: Some(param_did) }, ) }, - try_unify_abstract_consts: const_evaluatable::try_unify_abstract_consts, + try_unify_abstract_consts: |tcx, param_env_and| { + let (param_env, (a, b)) = param_env_and.into_parts(); + const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env) + }, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 8af4606db8520..fe369ca5c461c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -643,7 +643,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = (c1.val(), c2.val()) { - if self.infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) { + if self.infcx.try_unify_abstract_consts( + a.shrink(), + b.shrink(), + obligation.param_env, + ) { return Ok(EvaluatedToOk); } } diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index 1849ece9f76d9..57b642dc38fab 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -243,7 +243,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( ( ty::PredicateKind::ConstEvaluatable(a), ty::PredicateKind::ConstEvaluatable(b), - ) => tcx.try_unify_abstract_consts((a, b)), + ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))), ( ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)), ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)), diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs index 67d813819e7a3..71c164ab0a5bf 100644 --- a/src/test/ui/const-generics/issues/issue-83765.rs +++ b/src/test/ui/const-generics/issues/issue-83765.rs @@ -2,103 +2,115 @@ #![allow(incomplete_features)] trait TensorDimension { - const DIM : usize; - //~^ ERROR cycle detected when resolving instance ` - const ISSCALAR : bool = Self::DIM == 0; - fn is_scalar(&self) -> bool {Self::ISSCALAR} + const DIM: usize; + //~^ ERROR cycle detected when resolving instance + // FIXME Given the current state of the compiler its expected that we cycle here, + // but the cycle is still wrong. + const ISSCALAR: bool = Self::DIM == 0; + fn is_scalar(&self) -> bool { + Self::ISSCALAR + } } -trait TensorSize : TensorDimension { - fn size(&self) -> [usize;Self::DIM]; - fn inbounds(&self,index : [usize;Self::DIM]) -> bool { - index.iter().zip(self.size().iter()).all(|(i,s)| i < s) +trait TensorSize: TensorDimension { + fn size(&self) -> [usize; Self::DIM]; + fn inbounds(&self, index: [usize; Self::DIM]) -> bool { + index.iter().zip(self.size().iter()).all(|(i, s)| i < s) } } - trait Broadcastable: TensorSize + Sized { type Element; - fn bget(&self, index:[usize;Self::DIM]) -> Option; - fn lazy_updim(&self, size : [usize;NEWDIM] ) -> - LazyUpdim - { - assert!(NEWDIM >= Self::DIM, - "Updimmed tensor cannot have fewer indices than the initial one."); - LazyUpdim {size,reference:&self} + fn bget(&self, index: [usize; Self::DIM]) -> Option; + fn lazy_updim( + &self, + size: [usize; NEWDIM], + ) -> LazyUpdim { + assert!( + NEWDIM >= Self::DIM, + "Updimmed tensor cannot have fewer indices than the initial one." + ); + LazyUpdim { size, reference: &self } } - fn bmap T>(&self,foo : F) -> BMap{ - BMap {reference:self,closure : foo} + fn bmap T>(&self, foo: F) -> BMap { + BMap { reference: self, closure: foo } } } - -struct LazyUpdim<'a,T : Broadcastable,const OLDDIM : usize, const DIM : usize> { - size : [usize;DIM], - reference : &'a T +struct LazyUpdim<'a, T: Broadcastable, const OLDDIM: usize, const DIM: usize> { + size: [usize; DIM], + reference: &'a T, } -impl<'a,T : Broadcastable,const DIM : usize> TensorDimension for LazyUpdim<'a,T,{T::DIM},DIM> { - const DIM : usize = DIM; +impl<'a, T: Broadcastable, const DIM: usize> TensorDimension for LazyUpdim<'a, T, { T::DIM }, DIM> { + const DIM: usize = DIM; } -impl<'a,T : Broadcastable,const DIM : usize> TensorSize for LazyUpdim<'a,T,{T::DIM},DIM> { - fn size(&self) -> [usize;DIM] {self.size} +impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T::DIM }, DIM> { + fn size(&self) -> [usize; DIM] { + self.size + } } -impl<'a,T : Broadcastable,const DIM : usize> Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> -{ +impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> { type Element = T::Element; - fn bget(&self,index:[usize;DIM]) -> Option { + fn bget(&self, index: [usize; DIM]) -> Option { assert!(DIM >= T::DIM); - if !self.inbounds(index) {return None} + if !self.inbounds(index) { + return None; + } let size = self.size(); - let newindex : [usize;T::DIM] = Default::default(); + let newindex: [usize; T::DIM] = Default::default(); self.reference.bget(newindex) } } -struct BMap<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , const DIM: usize> { - reference : &'a T, - closure : F +struct BMap<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> { + reference: &'a T, + closure: F, } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R, - const DIM: usize> TensorDimension for BMap<'a,R,T,F,DIM> { - - const DIM : usize = DIM; +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorDimension + for BMap<'a, R, T, F, DIM> +{ + const DIM: usize = DIM; } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , - const DIM: usize> TensorSize for BMap<'a,R,T,F,DIM> { - - fn size(&self) -> [usize;DIM] {self.reference.size()} +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorSize + for BMap<'a, R, T, F, DIM> +{ + fn size(&self) -> [usize; DIM] { + self.reference.size() + } } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , - const DIM: usize> Broadcastable for BMap<'a,R,T,F,DIM> { - +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> Broadcastable + for BMap<'a, R, T, F, DIM> +{ type Element = R; - fn bget(&self,index:[usize;DIM]) -> Option { + fn bget(&self, index: [usize; DIM]) -> Option { self.reference.bget(index).map(&self.closure) } } impl TensorDimension for Vec { - const DIM : usize = 1; + const DIM: usize = 1; } impl TensorSize for Vec { - fn size(&self) -> [usize;1] {[self.len()]} + fn size(&self) -> [usize; 1] { + [self.len()] + } } impl Broadcastable for Vec { type Element = T; - fn bget(& self,index : [usize;1]) -> Option { + fn bget(&self, index: [usize; 1]) -> Option { self.get(index[0]).cloned() } } fn main() { - let v = vec![1,2,3]; - let bv = v.lazy_updim([3,4]); - let bbv = bv.bmap(|x| x*x); + let v = vec![1, 2, 3]; + let bv = v.lazy_updim([3, 4]); + let bbv = bv.bmap(|x| x * x); - println!("The size of v is {:?}",bbv.bget([0,2]).expect("Out of bounds.")); + println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds.")); } diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr index 0003969f7ac2d..8705a39fa4bcd 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -1,16 +1,16 @@ -error[E0391]: cycle detected when resolving instance ` as TensorDimension>::DIM` +error[E0391]: cycle detected when resolving instance ` as TensorDimension>::DIM` --> $DIR/issue-83765.rs:5:5 | -LL | const DIM : usize; - | ^^^^^^^^^^^^^^^^^^ +LL | const DIM: usize; + | ^^^^^^^^^^^^^^^^^ | note: ...which requires checking if `TensorDimension` fulfills its obligations... --> $DIR/issue-83765.rs:4:1 | LL | trait TensorDimension { | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires resolving instance ` as TensorDimension>::DIM`, completing the cycle - = note: cycle used when normalizing ` as TensorDimension>::DIM` + = note: ...which again requires resolving instance ` as TensorDimension>::DIM`, completing the cycle + = note: cycle used when normalizing ` as TensorDimension>::DIM` error: aborting due to previous error From fe69a5cf0cba9943311b350af0bc800f52c6adbd Mon Sep 17 00:00:00 2001 From: b-naber Date: Tue, 22 Mar 2022 15:27:20 +0100 Subject: [PATCH 10/15] dont canonicalize in try_unify_abstract_consts and erase regions instead --- compiler/rustc_infer/src/infer/mod.rs | 18 +++++----- .../src/traits/const_evaluatable.rs | 33 +++++++------------ 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 45502c720ae79..a779e38a16989 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -70,7 +70,6 @@ mod sub; pub mod type_variable; mod undo_log; -use crate::infer::canonical::OriginalQueryValues; pub use rustc_middle::infer::unify_key; #[must_use] @@ -695,14 +694,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ) -> bool { // Reject any attempt to unify two unevaluated constants that contain inference // variables, since inference variables in queries lead to ICEs. - if a.substs.has_infer_types_or_consts() || b.substs.has_infer_types_or_consts() { - debug!("a or b contain infer vars in its substs -> cannot unify"); + if a.substs.has_infer_types_or_consts() + || b.substs.has_infer_types_or_consts() + || param_env.has_infer_types_or_consts() + { + debug!("a or b or param_env contain infer vars in its substs -> cannot unify"); return false; } - let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default()); + let erased_args = self.tcx.erase_regions((a, b)); + let erased_param_env = self.tcx.erase_regions(param_env); + debug!("after erase_regions args: {:?}, param_env: {:?}", erased_args, param_env); - self.tcx.try_unify_abstract_consts(param_env.and(canonical.value)) + self.tcx.try_unify_abstract_consts(erased_param_env.and(erased_args)) } pub fn is_in_snapshot(&self) -> bool { @@ -1619,9 +1623,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // variables if substs.has_infer_types_or_consts() { debug!("substs have infer types or consts: {:?}", substs); - if substs.has_infer_types_or_consts() { - return Err(ErrorHandled::TooGeneric); - } + return Err(ErrorHandled::TooGeneric); } let param_env_erased = self.tcx.erase_regions(param_env); diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index c44e961363048..93810cb00cfdd 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -195,11 +195,13 @@ fn satisfied_from_param_env<'tcx>( match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(uv) => { if let Some(b_ct) = AbstractConst::new(tcx, uv)? { + let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); + // Try to unify with each subtree in the AbstractConst to allow for // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` // predicate for `(N + 1) * 2` let result = walk_abstract_const(tcx, b_ct, |b_ct| { - match try_unify(tcx, ct, b_ct, param_env) { + match const_unify_ctxt.try_unify(ct, b_ct) { true => ControlFlow::BREAK, false => ControlFlow::CONTINUE, } @@ -569,18 +571,6 @@ pub(super) fn thir_abstract_const<'tcx>( } } -/// Tries to unify two abstract constants using structural equality. -#[instrument(skip(tcx), level = "debug")] -pub(super) fn try_unify<'tcx>( - tcx: TyCtxt<'tcx>, - a: AbstractConst<'tcx>, - b: AbstractConst<'tcx>, - param_env: ty::ParamEnv<'tcx>, -) -> bool { - let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); - const_unify_ctxt.try_unify_inner(a, b) -} - pub(super) fn try_unify_abstract_consts<'tcx>( tcx: TyCtxt<'tcx>, (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), @@ -589,7 +579,8 @@ pub(super) fn try_unify_abstract_consts<'tcx>( (|| { if let Some(a) = AbstractConst::new(tcx, a)? { if let Some(b) = AbstractConst::new(tcx, b)? { - return Ok(try_unify(tcx, a, b, param_env)); + let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); + return Ok(const_unify_ctxt.try_unify(a, b)); } } @@ -666,7 +657,7 @@ impl<'tcx> ConstUnifyCtxt<'tcx> { /// Tries to unify two abstract constants using structural equality. #[instrument(skip(self), level = "debug")] - fn try_unify_inner(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { + fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { let a = if let Some(a) = self.try_replace_substs_in_root(a) { a } else { @@ -723,23 +714,23 @@ impl<'tcx> ConstUnifyCtxt<'tcx> { } } (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - self.try_unify_inner(a.subtree(al), b.subtree(bl)) - && self.try_unify_inner(a.subtree(ar), b.subtree(br)) + self.try_unify(a.subtree(al), b.subtree(bl)) + && self.try_unify(a.subtree(ar), b.subtree(br)) } (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - self.try_unify_inner(a.subtree(av), b.subtree(bv)) + self.try_unify(a.subtree(av), b.subtree(bv)) } (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) if a_args.len() == b_args.len() => { - self.try_unify_inner(a.subtree(a_f), b.subtree(b_f)) + self.try_unify(a.subtree(a_f), b.subtree(b_f)) && iter::zip(a_args, b_args) - .all(|(&an, &bn)| self.try_unify_inner(a.subtree(an), b.subtree(bn))) + .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) } (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) if (a_ty == b_ty) && (a_kind == b_kind) => { - self.try_unify_inner(a.subtree(a_operand), b.subtree(b_operand)) + self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) } // use this over `_ => false` to make adding variants to `Node` less error prone (Node::Cast(..), _) From 11a70dbc8ac49416598b0ff4525096845a0dc498 Mon Sep 17 00:00:00 2001 From: b-naber Date: Tue, 22 Mar 2022 16:13:28 +0100 Subject: [PATCH 11/15] erase region in ParamEnvAnd and make ConstUnifyCtxt private --- compiler/rustc_infer/src/infer/mod.rs | 8 ++++---- compiler/rustc_middle/src/mir/interpret/queries.rs | 5 ++++- .../src/traits/const_evaluatable.rs | 12 ++++-------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index a779e38a16989..2886d921c705d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -702,11 +702,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return false; } - let erased_args = self.tcx.erase_regions((a, b)); - let erased_param_env = self.tcx.erase_regions(param_env); - debug!("after erase_regions args: {:?}, param_env: {:?}", erased_args, param_env); + let param_env_and = param_env.and((a, b)); + let erased = self.tcx.erase_regions(param_env_and); + debug!("after erase_regions: {:?}", erased); - self.tcx.try_unify_abstract_consts(erased_param_env.and(erased_args)) + self.tcx.try_unify_abstract_consts(erased) } pub fn is_in_snapshot(&self) -> bool { diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 639a9bc600ac4..7e5989b4112cf 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -41,7 +41,10 @@ impl<'tcx> TyCtxt<'tcx> { ) -> EvalToConstValueResult<'tcx> { // Cannot resolve `Unevaluated` constants that contain inference // variables. We reject those here since `resolve_opt_const_arg` - // would fail otherwise + // would fail otherwise. + // + // When trying to evaluate constants containing inference variables, + // use `Infcx::const_eval_resolve` instead. if ct.substs.has_infer_types_or_consts() { bug!("did not expect inference variables here"); } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 93810cb00cfdd..4fb186ab0efe8 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -195,7 +195,7 @@ fn satisfied_from_param_env<'tcx>( match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(uv) => { if let Some(b_ct) = AbstractConst::new(tcx, uv)? { - let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; // Try to unify with each subtree in the AbstractConst to allow for // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` @@ -579,7 +579,7 @@ pub(super) fn try_unify_abstract_consts<'tcx>( (|| { if let Some(a) = AbstractConst::new(tcx, a)? { if let Some(b) = AbstractConst::new(tcx, b)? { - let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; return Ok(const_unify_ctxt.try_unify(a, b)); } } @@ -625,22 +625,18 @@ where recurse(tcx, ct, &mut f) } -pub(super) struct ConstUnifyCtxt<'tcx> { +struct ConstUnifyCtxt<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, } impl<'tcx> ConstUnifyCtxt<'tcx> { - pub(super) fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { - ConstUnifyCtxt { tcx, param_env } - } - // Substitutes generics repeatedly to allow AbstractConsts to unify where a // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] #[inline] #[instrument(skip(self), level = "debug")] - pub(super) fn try_replace_substs_in_root( + fn try_replace_substs_in_root( &self, mut abstr_const: AbstractConst<'tcx>, ) -> Option> { From fe40240e4d4154c349e9c76514badbfed7a0a0cf Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Sat, 5 Mar 2022 20:37:04 -0500 Subject: [PATCH 12/15] Clarify which kinds of MIR are allowed during which phases. This enhances documentation with these details and extends the validator to check these requirements more thoroughly. As a part of this, we add a new `Deaggregated` phase, and rename other phases so that their names more naturally correspond to what they represent. --- .../src/transform/promote_consts.rs | 2 +- .../src/transform/validate.rs | 74 ++++++++++++------- compiler/rustc_middle/src/mir/mod.rs | 56 +++++++++----- .../rustc_mir_transform/src/deaggregator.rs | 4 + .../src/elaborate_drops.rs | 2 +- compiler/rustc_mir_transform/src/generator.rs | 2 +- compiler/rustc_mir_transform/src/lib.rs | 8 +- .../rustc_mir_transform/src/pass_manager.rs | 2 +- 8 files changed, 98 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 30764f689c95c..faea2111d9210 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -42,7 +42,7 @@ pub struct PromoteTemps<'tcx> { impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { fn phase_change(&self) -> Option { - Some(MirPhase::ConstPromotion) + Some(MirPhase::ConstsPromoted) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index cf15fc4ddc3a5..deeca78b75d99 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -266,22 +266,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } - // The deaggregator currently does not deaggreagate arrays. - // So for now, we ignore them here. - Rvalue::Aggregate(box AggregateKind::Array { .. }, _) => {} - // All other aggregates must be gone after some phases. - Rvalue::Aggregate(box kind, _) => { - if self.mir_phase > MirPhase::DropLowering - && !matches!(kind, AggregateKind::Generator(..)) - { - // Generators persist until the state machine transformation, but all - // other aggregates must have been lowered. - self.fail( - location, - format!("{:?} have been lowered to field assignments", rvalue), - ) - } else if self.mir_phase > MirPhase::GeneratorLowering { - // No more aggregates after drop and generator lowering. + Rvalue::Aggregate(agg_kind, _) => { + let disallowed = match **agg_kind { + AggregateKind::Array(..) => false, + AggregateKind::Generator(..) => { + self.mir_phase >= MirPhase::GeneratorsLowered + } + _ => self.mir_phase >= MirPhase::Deaggregated, + }; + if disallowed { self.fail( location, format!("{:?} have been lowered to field assignments", rvalue), @@ -289,7 +282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } Rvalue::Ref(_, BorrowKind::Shallow, _) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase", @@ -300,7 +293,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::AscribeUserType(..) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`AscribeUserType` should have been removed after drop lowering phase", @@ -308,7 +301,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::FakeRead(..) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`FakeRead` should have been removed after drop lowering phase", @@ -351,10 +344,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty)) } } - StatementKind::SetDiscriminant { .. } - | StatementKind::StorageLive(..) + StatementKind::SetDiscriminant { .. } => { + if self.mir_phase < MirPhase::DropsLowered { + self.fail(location, "`SetDiscriminant` is not allowed until drop elaboration"); + } + } + StatementKind::Retag(_, _) => { + // FIXME(JakobDegen) The validator should check that `self.mir_phase < + // DropsLowered`. However, this causes ICEs with generation of drop shims, which + // seem to fail to set their `MirPhase` correctly. + } + StatementKind::StorageLive(..) | StatementKind::StorageDead(..) - | StatementKind::Retag(_, _) | StatementKind::Coverage(_) | StatementKind::Nop => {} } @@ -424,10 +425,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::DropAndReplace { target, unwind, .. } => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, - "`DropAndReplace` is not permitted to exist after drop elaboration", + "`DropAndReplace` should have been removed during drop elaboration", ); } self.check_edge(location, *target, EdgeKind::Normal); @@ -494,7 +495,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::Yield { resume, drop, .. } => { - if self.mir_phase > MirPhase::GeneratorLowering { + if self.mir_phase >= MirPhase::GeneratorsLowered { self.fail(location, "`Yield` should have been replaced by generator lowering"); } self.check_edge(location, *resume, EdgeKind::Normal); @@ -503,10 +504,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::FalseEdge { real_target, imaginary_target } => { + if self.mir_phase >= MirPhase::DropsLowered { + self.fail( + location, + "`FalseEdge` should have been removed after drop elaboration", + ); + } self.check_edge(location, *real_target, EdgeKind::Normal); self.check_edge(location, *imaginary_target, EdgeKind::Normal); } TerminatorKind::FalseUnwind { real_target, unwind } => { + if self.mir_phase >= MirPhase::DropsLowered { + self.fail( + location, + "`FalseUnwind` should have been removed after drop elaboration", + ); + } self.check_edge(location, *real_target, EdgeKind::Normal); if let Some(unwind) = unwind { self.check_edge(location, *unwind, EdgeKind::Unwind); @@ -520,12 +533,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.check_edge(location, *cleanup, EdgeKind::Unwind); } } + TerminatorKind::GeneratorDrop => { + if self.mir_phase >= MirPhase::GeneratorsLowered { + self.fail( + location, + "`GeneratorDrop` should have been replaced by generator lowering", + ); + } + } // Nothing to validate for these. TerminatorKind::Resume | TerminatorKind::Abort | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::GeneratorDrop => {} + | TerminatorKind::Unreachable => {} } self.super_terminator(terminator, location); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index a5468b3f4f202..59cd7d7dda7f3 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -127,14 +127,11 @@ pub trait MirPass<'tcx> { /// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the /// dialects forbid certain variants or values in certain phases. /// -/// Note: Each phase's validation checks all invariants of the *previous* phases' dialects. A phase -/// that changes the dialect documents what invariants must be upheld *after* that phase finishes. -/// /// Warning: ordering of variants is significant. #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(HashStable)] pub enum MirPhase { - Build = 0, + Built = 0, // FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query). // We used to have this for pre-miri MIR based const eval. Const = 1, @@ -142,17 +139,32 @@ pub enum MirPhase { /// by creating a new MIR body per promoted element. After this phase (and thus the termination /// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir` /// query. - ConstPromotion = 2, - /// After this phase - /// * the only `AggregateKind`s allowed are `Array` and `Generator`, - /// * `DropAndReplace` is gone for good - /// * `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` terminator - /// means that the auto-generated drop glue will be invoked. - DropLowering = 3, - /// After this phase, generators are explicit state machines (no more `Yield`). - /// `AggregateKind::Generator` is gone for good. - GeneratorLowering = 4, - Optimization = 5, + ConstsPromoted = 2, + /// Beginning with this phase, the following variants are disallowed: + /// * [`TerminatorKind::DropAndReplace`](terminator::TerminatorKind::DropAndReplace) + /// * [`TerminatorKind::FalseUnwind`](terminator::TerminatorKind::FalseUnwind) + /// * [`TerminatorKind::FalseEdge`](terminator::TerminatorKind::FalseEdge) + /// * [`StatementKind::FakeRead`] + /// * [`StatementKind::AscribeUserType`] + /// * [`Rvalue::Ref`] with `BorrowKind::Shallow` + /// + /// And the following variant is allowed: + /// * [`StatementKind::Retag`] + /// + /// Furthermore, `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` + /// terminator means that the auto-generated drop glue will be invoked. + DropsLowered = 3, + /// Beginning with this phase, the following variant is disallowed: + /// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array` + /// + /// And the following variant is allowed: + /// * [`StatementKind::SetDiscriminant`] + Deaggregated = 4, + /// Beginning with this phase, the following variants are disallowed: + /// * [`TerminatorKind::Yield`](terminator::TerminatorKind::Yield) + /// * [`TerminatorKind::GeneratorDrop](terminator::TerminatorKind::GeneratorDrop) + GeneratorsLowered = 5, + Optimized = 6, } impl MirPhase { @@ -311,7 +323,7 @@ impl<'tcx> Body<'tcx> { ); let mut body = Body { - phase: MirPhase::Build, + phase: MirPhase::Built, source, basic_blocks, source_scopes, @@ -346,7 +358,7 @@ impl<'tcx> Body<'tcx> { /// crate. pub fn new_cfg_only(basic_blocks: IndexVec>) -> Self { let mut body = Body { - phase: MirPhase::Build, + phase: MirPhase::Built, source: MirSource::item(DefId::local(CRATE_DEF_INDEX)), basic_blocks, source_scopes: IndexVec::new(), @@ -1541,6 +1553,11 @@ impl Statement<'_> { } } +/// The various kinds of statements that can appear in MIR. +/// +/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which +/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, +/// causing an ICE if they are violated. #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] pub enum StatementKind<'tcx> { /// Write the RHS Rvalue to the LHS Place. @@ -2223,6 +2240,11 @@ impl<'tcx> Operand<'tcx> { /// Rvalues #[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)] +/// The various kinds of rvalues that can appear in MIR. +/// +/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which +/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, +/// causing an ICE if they are violated. pub enum Rvalue<'tcx> { /// x (either a move or copy, depending on type of x) Use(Operand<'tcx>), diff --git a/compiler/rustc_mir_transform/src/deaggregator.rs b/compiler/rustc_mir_transform/src/deaggregator.rs index 44753c5f631ca..616ba819982ec 100644 --- a/compiler/rustc_mir_transform/src/deaggregator.rs +++ b/compiler/rustc_mir_transform/src/deaggregator.rs @@ -6,6 +6,10 @@ use rustc_middle::ty::TyCtxt; pub struct Deaggregator; impl<'tcx> MirPass<'tcx> for Deaggregator { + fn phase_change(&self) -> Option { + Some(MirPhase::Deaggregated) + } + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let local_decls = &*local_decls; diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index a4b1d86ff610e..f78c7a084d89a 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -20,7 +20,7 @@ pub struct ElaborateDrops; impl<'tcx> MirPass<'tcx> for ElaborateDrops { fn phase_change(&self) -> Option { - Some(MirPhase::DropLowering) + Some(MirPhase::DropsLowered) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 8bc7f5e9ce2e0..ad96bf544cbd3 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -1235,7 +1235,7 @@ fn create_cases<'tcx>( impl<'tcx> MirPass<'tcx> for StateTransform { fn phase_change(&self) -> Option { - Some(MirPhase::GeneratorLowering) + Some(MirPhase::GeneratorsLowered) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 3b2332a6e3142..a6436ae515451 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -341,7 +341,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) - pm::run_passes( tcx, &mut body, - &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimization)], + &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimized)], ); } } @@ -398,7 +398,7 @@ fn mir_drops_elaborated_and_const_checked<'tcx>( } run_post_borrowck_cleanup_passes(tcx, &mut body); - assert!(body.phase == MirPhase::DropLowering); + assert!(body.phase == MirPhase::Deaggregated); tcx.alloc_steal_mir(body) } @@ -458,7 +458,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { ], ); - assert!(body.phase == MirPhase::GeneratorLowering); + assert!(body.phase == MirPhase::GeneratorsLowered); // The main optimizations that we do on MIR. pm::run_passes( @@ -495,7 +495,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &deduplicate_blocks::DeduplicateBlocks, // Some cleanup necessary at least for LLVM and potentially other codegen backends. &add_call_guards::CriticalCallEdges, - &marker::PhaseChange(MirPhase::Optimization), + &marker::PhaseChange(MirPhase::Optimized), // Dump the end result for testing and debugging purposes. &dump_mir::Marker("PreCodegen"), ], diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 8725eae870917..740a2168b41cc 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -114,7 +114,7 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn } } - if validate || body.phase == MirPhase::Optimization { + if validate || body.phase == MirPhase::Optimized { validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase)); } } From 26d7b8ddb2d323e2d6cb77c04caf7c58ce5af267 Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Sun, 6 Mar 2022 15:53:23 -0500 Subject: [PATCH 13/15] Clarify more MIR docs --- compiler/rustc_middle/src/mir/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 59cd7d7dda7f3..ea71cff161612 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1561,6 +1561,8 @@ impl Statement<'_> { #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] pub enum StatementKind<'tcx> { /// Write the RHS Rvalue to the LHS Place. + /// + /// The LHS place may not overlap with any memory accessed on the RHS. Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>), /// This represents all the reading that a pattern match may do @@ -1778,6 +1780,19 @@ static_assert_size!(Place<'_>, 16); pub enum ProjectionElem { Deref, Field(Field, T), + /// Index into a slice/array. + /// + /// Note that this does not also dereference, and so it does not exactly correspond to slice + /// indexing in Rust. In other words, in the below Rust code: + /// + /// ```rust + /// let x = &[1, 2, 3, 4]; + /// let i = 2; + /// x[i]; + /// ``` + /// + /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same + /// thing is true of the `ConstantIndex` and `Subslice` projections below. Index(V), /// These indices are generated by slice patterns. Easiest to explain From e169261a6fc4410cd1ff9cdc37da6ef3cc5e0600 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 24 Mar 2022 10:59:28 +0100 Subject: [PATCH 14/15] debuginfo: Fix debuginfo for Box where T is unsized. Before this fix, the debuginfo for the fields was generated from the struct defintion of Box, but (at least at the moment) the compiler pretends that Box is just a (fat) pointer, so the fields need to be `pointer` and `vtable` instead of `__0: Unique` and `__1: Allocator`. This is meant as a temporary mitigation until we can make sure that simply treating Box as a regular struct in debuginfo does not cause too much breakage in the ecosystem. --- .../src/debuginfo/metadata.rs | 19 ++++++++++++++++++- src/test/debuginfo/unsized.rs | 19 +++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index f02c7b2d2e11a..74e194750fadd 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -166,6 +166,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( pointee_type: Ty<'tcx>, unique_type_id: UniqueTypeId<'tcx>, ) -> DINodeCreationResult<'ll> { + // The debuginfo generated by this function is only valid if `ptr_type` is really just + // a (fat) pointer. Make sure it is not called for e.g. `Box`. + debug_assert_eq!( + cx.size_and_align_of(ptr_type), + cx.size_and_align_of(cx.tcx.mk_mut_ptr(pointee_type)) + ); + let pointee_type_di_node = type_di_node(cx, pointee_type); return_if_di_node_created_in_meantime!(cx, unique_type_id); @@ -212,7 +219,17 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( DIFlags::FlagZero, ), |cx, owner| { - let layout = cx.layout_of(ptr_type); + // FIXME: If this fat pointer is a `Box` then we don't want to use its + // type layout and instead use the layout of the raw pointer inside + // of it. + // The proper way to handle this is to not treat Box as a pointer + // at all and instead emit regular struct debuginfo for it. We just + // need to make sure that we don't break existing debuginfo consumers + // by doing that (at least not without a warning period). + let layout_type = + if ptr_type.is_box() { cx.tcx.mk_mut_ptr(pointee_type) } else { ptr_type }; + + let layout = cx.layout_of(layout_type); let addr_field = layout.field(cx, abi::FAT_PTR_ADDR); let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA); diff --git a/src/test/debuginfo/unsized.rs b/src/test/debuginfo/unsized.rs index 7ccc88ef940dc..7cb0002ca5125 100644 --- a/src/test/debuginfo/unsized.rs +++ b/src/test/debuginfo/unsized.rs @@ -16,13 +16,17 @@ // gdbg-check:$3 = {pointer = [...], vtable = [...]} // gdbr-check:$3 = &unsized::Foo {pointer: [...], vtable: [...]} +// gdb-command:print _box +// gdbg-check:$4 = {pointer = [...], vtable = [...]} +// gdbr-check:$4 = alloc::boxed::Box, alloc::alloc::Global> {pointer: [...], vtable: [...]} + // gdb-command:print tuple_slice -// gdbg-check:$4 = {data_ptr = [...], length = 2} -// gdbr-check:$4 = &(i32, i32, [i32]) {data_ptr: [...], length: 2} +// gdbg-check:$5 = {data_ptr = [...], length = 2} +// gdbr-check:$5 = &(i32, i32, [i32]) {data_ptr: [...], length: 2} // gdb-command:print tuple_dyn -// gdbg-check:$5 = {pointer = [...], vtable = [...]} -// gdbr-check:$5 = &(i32, i32, dyn core::fmt::Debug) {pointer: [...], vtable: [...]} +// gdbg-check:$6 = {pointer = [...], vtable = [...]} +// gdbr-check:$6 = &(i32, i32, dyn core::fmt::Debug) {pointer: [...], vtable: [...]} // === CDB TESTS =================================================================================== @@ -42,6 +46,12 @@ // cdb-check: [+0x000] pointer : 0x[...] [Type: unsized::Foo > *] // cdb-check: [...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] +// cdb-command:dx _box +// cdb-check: +// cdb-check:_box [Type: alloc::boxed::Box >,alloc::alloc::Global>] +// cdb-check:[+0x000] pointer : 0x[...] [Type: unsized::Foo > *] +// cdb-check:[...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] + // cdb-command:dx tuple_slice // cdb-check:tuple_slice [Type: ref$ > >] // cdb-check: [+0x000] data_ptr : 0x[...] [Type: tuple$ > *] @@ -69,6 +79,7 @@ fn main() { let a: &Foo<[u8]> = &foo.value; let b: &Foo> = &foo; let c: &Foo = &Foo { value: 7i32 }; + let _box: Box> = Box::new(Foo { value: 8i32 }); // Also check unsized tuples let tuple_slice: &(i32, i32, [i32]) = &(0, 1, [2, 3]); From 85e67b9a5917bc82bdba0aaa3a18595904cee629 Mon Sep 17 00:00:00 2001 From: lightning1141 Date: Sat, 26 Feb 2022 11:55:07 +0800 Subject: [PATCH 15/15] Fix ice when error reporting recursion errors Fixes: #90319, #92148, #93955 --- compiler/rustc_middle/src/traits/mod.rs | 2 +- compiler/rustc_middle/src/traits/select.rs | 15 ++++++++++++++- .../src/traits/error_reporting/mod.rs | 9 +++++++-- .../src/traits/project.rs | 5 ++++- .../src/traits/query/evaluate_obligation.rs | 2 ++ .../src/traits/select/candidate_assembly.rs | 3 ++- .../src/traits/select/mod.rs | 18 ++++++++++-------- src/test/ui/typeck/issue-90319.rs | 17 +++++++++++++++++ src/test/ui/typeck/issue-90319.stderr | 9 +++++++++ 9 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 src/test/ui/typeck/issue-90319.rs create mode 100644 src/test/ui/typeck/issue-90319.stderr diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 36bbccf0a8038..0707fa2efea70 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -478,7 +478,7 @@ pub enum SelectionError<'tcx> { /// A given constant couldn't be evaluated. NotConstEvaluatable(NotConstEvaluatable), /// Exceeded the recursion depth during type projection. - Overflow, + Overflow(OverflowError), /// Signaling that an error has already been emitted, to avoid /// multiple errors being shown. ErrorReporting, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 56d42706f6778..5297825a92fcc 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -5,6 +5,7 @@ use self::EvaluationResult::*; use super::{SelectionError, SelectionResult}; +use rustc_errors::ErrorGuaranteed; use crate::ty; @@ -264,14 +265,26 @@ impl EvaluationResult { /// Indicates that trait evaluation caused overflow and in which pass. #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable)] pub enum OverflowError { + Error(ErrorGuaranteed), Canonical, ErrorReporting, } +impl From for OverflowError { + fn from(e: ErrorGuaranteed) -> OverflowError { + OverflowError::Error(e) + } +} + +TrivialTypeFoldableAndLiftImpls! { + OverflowError, +} + impl<'tcx> From for SelectionError<'tcx> { fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { match overflow_error { - OverflowError::Canonical => SelectionError::Overflow, + OverflowError::Error(e) => SelectionError::Overflow(OverflowError::Error(e)), + OverflowError::Canonical => SelectionError::Overflow(OverflowError::Canonical), OverflowError::ErrorReporting => SelectionError::ErrorReporting, } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 229e108d5d640..5e220173caeea 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -22,6 +22,7 @@ use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; use rustc_middle::thir::abstract_const::NotConstEvaluatable; +use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::{ @@ -928,8 +929,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error"); return; } - - Overflow => { + // Already reported. + Overflow(OverflowError::Error(_)) => { + self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported"); + return; + } + Overflow(_) => { bug!("overflow should be handled before the `report_selection_error` path"); } SelectionError::ErrorReporting => { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 390381752f95d..b61e68735712b 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -27,6 +27,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; @@ -1139,7 +1140,9 @@ fn project<'cx, 'tcx>( if !selcx.tcx().recursion_limit().value_within_limit(obligation.recursion_depth) { // This should really be an immediate error, but some existing code // relies on being able to recover from this. - return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow)); + return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow( + OverflowError::Canonical, + ))); } if obligation.predicate.references_error() { diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 4874ba6f58cac..db45ee3fed7db 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -108,9 +108,11 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { ) } OverflowError::ErrorReporting => EvaluationResult::EvaluatedToErr, + OverflowError::Error(_) => EvaluationResult::EvaluatedToErr, }) } Err(OverflowError::ErrorReporting) => EvaluationResult::EvaluatedToErr, + Err(OverflowError::Error(_)) => EvaluationResult::EvaluatedToErr, } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index c0a283d2edae4..3e7a2252318be 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -164,8 +164,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) } Ok(_) => Ok(None), - Err(OverflowError::Canonical) => Err(Overflow), + Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)), Err(OverflowError::ErrorReporting) => Err(ErrorReporting), + Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))), }) .flat_map(Result::transpose) .collect::, _>>()?; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4135fbca06099..a988a51c83964 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -29,7 +29,7 @@ use crate::traits::project::ProjectionCacheKeyExt; use crate::traits::ProjectionCacheKey; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::Diagnostic; +use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::LateBoundRegionConversionTime; @@ -320,11 +320,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { let candidate = match self.select_from_obligation(obligation) { - Err(SelectionError::Overflow) => { + Err(SelectionError::Overflow(OverflowError::Canonical)) => { // In standard mode, overflow must have been caught and reported // earlier. assert!(self.query_mode == TraitQueryMode::Canonical); - return Err(SelectionError::Overflow); + return Err(SelectionError::Overflow(OverflowError::Canonical)); } Err(SelectionError::Ambiguous(_)) => { return Ok(None); @@ -339,9 +339,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; match self.confirm_candidate(obligation, candidate) { - Err(SelectionError::Overflow) => { + Err(SelectionError::Overflow(OverflowError::Canonical)) => { assert!(self.query_mode == TraitQueryMode::Canonical); - Err(SelectionError::Overflow) + Err(SelectionError::Overflow(OverflowError::Canonical)) } Err(e) => Err(e), Ok(candidate) => { @@ -958,7 +958,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(c)) => self.evaluate_candidate(stack, &c), Err(SelectionError::Ambiguous(_)) => Ok(EvaluatedToAmbig), Ok(None) => Ok(EvaluatedToAmbig), - Err(Overflow) => Err(OverflowError::Canonical), + Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical), Err(ErrorReporting) => Err(OverflowError::ErrorReporting), Err(..) => Ok(EvaluatedToErr), } @@ -1117,7 +1117,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match self.query_mode { TraitQueryMode::Standard => { if self.infcx.is_tainted_by_errors() { - return Err(OverflowError::ErrorReporting); + return Err(OverflowError::Error( + ErrorGuaranteed::unchecked_claim_error_was_emitted(), + )); } self.infcx.report_overflow_error(error_obligation, true); } @@ -1353,7 +1355,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } if self.can_use_global_caches(param_env) { - if let Err(Overflow) = candidate { + if let Err(Overflow(OverflowError::Canonical)) = candidate { // Don't cache overflow globally; we only produce this in certain modes. } else if !pred.needs_infer() { if !candidate.needs_infer() { diff --git a/src/test/ui/typeck/issue-90319.rs b/src/test/ui/typeck/issue-90319.rs new file mode 100644 index 0000000000000..57e6ac7cf34f5 --- /dev/null +++ b/src/test/ui/typeck/issue-90319.rs @@ -0,0 +1,17 @@ +struct Wrapper(T); + +trait Trait { + fn method(&self) {} +} + +impl<'a, T> Trait for Wrapper<&'a T> where Wrapper: Trait {} + +fn get() -> T { + unimplemented!() +} + +fn main() { + let thing = get::();//~ERROR cannot find type `Thing` in this scope [E0412] + let wrapper = Wrapper(thing); + Trait::method(&wrapper); +} diff --git a/src/test/ui/typeck/issue-90319.stderr b/src/test/ui/typeck/issue-90319.stderr new file mode 100644 index 0000000000000..61549dd701e74 --- /dev/null +++ b/src/test/ui/typeck/issue-90319.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `Thing` in this scope + --> $DIR/issue-90319.rs:14:23 + | +LL | let thing = get::(); + | ^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`.