From ee5404c69ecd5ea1500bd14b854a2b08d1196d21 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Thu, 9 Feb 2023 14:01:07 +0000 Subject: [PATCH 01/14] Add additional options to `x setup` --- src/bootstrap/builder.rs | 2 +- src/bootstrap/flags.rs | 13 ++++- src/bootstrap/setup.rs | 120 +++++++++++++++++++++++++++++++++------ 3 files changed, 114 insertions(+), 21 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index b4fc1d4f28da7..98998bfe5f28e 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -793,7 +793,7 @@ impl<'a> Builder<'a> { run::CollectLicenseMetadata, run::GenerateCopyright, ), - Kind::Setup => describe!(setup::Profile), + Kind::Setup => describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode), Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), // special-cased in Build::build() Kind::Format => vec![], diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 52c3dc0bf7591..30d96dd2d28f9 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -542,7 +542,8 @@ Arguments: Kind::Setup => { subcommand_help.push_str(&format!( "\n -x.py setup creates a `config.toml` which changes the defaults for x.py itself. +x.py setup creates a `config.toml` which changes the defaults for x.py itself, +as well as setting up a git pre-push hook, VS code config and toolchain link. Arguments: This subcommand accepts a 'profile' to use for builds. For example: @@ -552,7 +553,13 @@ Arguments: The profile is optional and you will be prompted interactively if it is not given. The following profiles are available: -{}", +{} + + To only set up the git hook, VS code or toolchain link, you may use + ./x.py setup hook + ./x.py setup vscode + ./x.py setup link +", Profile::all_for_help(" ").trim_end() )); } @@ -625,7 +632,7 @@ Arguments: } Kind::Setup => { let profile = if paths.len() > 1 { - eprintln!("\nerror: At most one profile can be passed to setup\n"); + eprintln!("\nerror: At most one option can be passed to setup\n"); usage(1, &opts, verbose, &subcommand_help) } else if let Some(path) = paths.pop() { let profile_string = t!(path.into_os_string().into_string().map_err( diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 2b613ad50ee48..28ae6e757895a 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -21,6 +21,7 @@ pub enum Profile { Library, Tools, User, + None, } /// A list of historical hashes of `src/etc/vscode_settings.json`. @@ -40,7 +41,7 @@ impl Profile { pub fn all() -> impl Iterator { use Profile::*; // N.B. these are ordered by how they are displayed, not alphabetically - [Library, Compiler, Codegen, Tools, User].iter().copied() + [Library, Compiler, Codegen, Tools, User, None].iter().copied() } pub fn purpose(&self) -> String { @@ -51,6 +52,7 @@ impl Profile { Codegen => "Contribute to the compiler, and also modify LLVM or codegen", Tools => "Contribute to tools which depend on the compiler, but do not modify it directly (e.g. rustdoc, clippy, miri)", User => "Install Rust from source", + None => "Do not modify `config.toml`" } .to_string() } @@ -70,6 +72,7 @@ impl Profile { Profile::Library => "library", Profile::Tools => "tools", Profile::User => "user", + Profile::None => "none", } } } @@ -86,6 +89,7 @@ impl FromStr for Profile { "tools" | "tool" | "rustdoc" | "clippy" | "miri" | "rustfmt" | "rls" => { Ok(Profile::Tools) } + "none" => Ok(Profile::None), _ => Err(format!("unknown profile: '{}'", s)), } } @@ -143,17 +147,8 @@ impl Step for Profile { } pub fn setup(config: &Config, profile: Profile) { - let stage_path = - ["build", config.build.rustc_target_arg(), "stage1"].join(&MAIN_SEPARATOR.to_string()); - - if !rustup_installed() && profile != Profile::User { - eprintln!("`rustup` is not installed; cannot link `stage1` toolchain"); - } else if stage_dir_exists(&stage_path[..]) && !config.dry_run() { - attempt_toolchain_link(&stage_path[..]); - } - - let suggestions = match profile { - Profile::Codegen | Profile::Compiler => &["check", "build", "test"][..], + let suggestions: &[&str] = match profile { + Profile::Codegen | Profile::Compiler | Profile::None => &["check", "build", "test"], Profile::Tools => &[ "check", "build", @@ -166,11 +161,6 @@ pub fn setup(config: &Config, profile: Profile) { Profile::User => &["dist", "build"], }; - if !config.dry_run() { - t!(install_git_hook_maybe(&config)); - t!(create_vscode_settings_maybe(&config)); - } - println!(); println!("To get started, try one of the following commands:"); @@ -189,6 +179,9 @@ pub fn setup(config: &Config, profile: Profile) { } fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) { + if profile == Profile::None { + return; + } if path.exists() { eprintln!(); eprintln!( @@ -216,6 +209,41 @@ fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) { println!("`x.py` will now use the configuration at {}", include_path.display()); } +/// Creates a toolchain link for stage1 using `rustup` +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub struct Link; +impl Step for Link { + type Output = (); + const DEFAULT: bool = true; + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("link") + } + fn make_run(run: RunConfig<'_>) { + if run.builder.config.dry_run() { + return; + } + if let [cmd] = &run.paths[..] { + if cmd.assert_single_path().path.as_path().as_os_str() == "link" { + run.builder.ensure(Link); + } + } + } + fn run(self, builder: &Builder<'_>) -> Self::Output { + let config = &builder.config; + if config.dry_run() { + return; + } + let stage_path = + ["build", config.build.rustc_target_arg(), "stage1"].join(&MAIN_SEPARATOR.to_string()); + + if !rustup_installed() { + eprintln!("`rustup` is not installed; cannot link `stage1` toolchain"); + } else if stage_dir_exists(&stage_path[..]) && !config.dry_run() { + attempt_toolchain_link(&stage_path[..]); + } + } +} + fn rustup_installed() -> bool { Command::new("rustup") .arg("--version") @@ -393,6 +421,35 @@ fn prompt_user(prompt: &str) -> io::Result> { } } +/// Installs `src/etc/pre-push.sh` as a Git hook +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub struct Hook; + +impl Step for Hook { + type Output = (); + const DEFAULT: bool = true; + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("hook") + } + fn make_run(run: RunConfig<'_>) { + if run.builder.config.dry_run() { + return; + } + if let [cmd] = &run.paths[..] { + if cmd.assert_single_path().path.as_path().as_os_str() == "hook" { + run.builder.ensure(Hook); + } + } + } + fn run(self, builder: &Builder<'_>) -> Self::Output { + let config = &builder.config; + if config.dry_run() { + return; + } + t!(install_git_hook_maybe(&config)); + } +} + // install a git hook to automatically run tidy, if they want fn install_git_hook_maybe(config: &Config) -> io::Result<()> { let git = t!(config.git().args(&["rev-parse", "--git-common-dir"]).output().map(|output| { @@ -431,6 +488,35 @@ undesirable, simply delete the `pre-push` file from .git/hooks." Ok(()) } +/// Sets up or displays `src/etc/vscode_settings.json` +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub struct Vscode; + +impl Step for Vscode { + type Output = (); + const DEFAULT: bool = true; + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("vscode") + } + fn make_run(run: RunConfig<'_>) { + if run.builder.config.dry_run() { + return; + } + if let [cmd] = &run.paths[..] { + if cmd.assert_single_path().path.as_path().as_os_str() == "vscode" { + run.builder.ensure(Vscode); + } + } + } + fn run(self, builder: &Builder<'_>) -> Self::Output { + let config = &builder.config; + if config.dry_run() { + return; + } + t!(create_vscode_settings_maybe(&config)); + } +} + /// Create a `.vscode/settings.json` file for rustc development, or just print it fn create_vscode_settings_maybe(config: &Config) -> io::Result<()> { let (current_hash, historical_hashes) = SETTINGS_HASHES.split_last().unwrap(); From 960ac2e07f2edc7ac23f006d0c07467ac0967833 Mon Sep 17 00:00:00 2001 From: lionelllohcd Date: Sat, 18 Feb 2023 23:34:06 -0800 Subject: [PATCH 02/14] [107049] Recognise top level keys in config.toml.example --- src/bootstrap/configure.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 0af329e7007ac..91af36d9070ff 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -379,8 +379,14 @@ def set(key, value): sections[None] = [] section_order = [None] targets = {} +top_level_keys = [] for line in open(rust_dir + '/config.toml.example').read().split("\n"): + if cur_section == None: + if line.count('=') == 1: + top_level_key = line.split('=')[0] + top_level_key = top_level_key.strip(' #') + top_level_keys.append(top_level_key) if line.startswith('['): cur_section = line[1:-1] if cur_section.startswith('target'): @@ -459,12 +465,23 @@ def configure_section(lines, config): raise RuntimeError("failed to find config line for {}".format(key)) -for section_key in config: - section_config = config[section_key] - if section_key not in sections: - raise RuntimeError("config key {} not in sections".format(section_key)) +def configure_top_level_key(lines, top_level_key, value): + for i, line in enumerate(lines): + if line.startswith('#' + top_level_key + ' = ') or line.startswith(top_level_key + ' = '): + lines[i] = "{} = {}".format(top_level_key, value) + return + + raise RuntimeError("failed to find config line for {}".format(top_level_key)) - if section_key == 'target': + +for section_key, section_config in config.items(): + if section_key not in sections and section_key not in top_level_keys: + raise RuntimeError("config key {} not in sections or top_level_keys".format(section_key)) + + if section_key in top_level_keys: + configure_top_level_key(sections[None], section_key, section_config) + + elif section_key == 'target': for target in section_config: configure_section(targets[target], section_config[target]) else: From 5643706ca2ca59a47966c03104d4b8d931adb2dd Mon Sep 17 00:00:00 2001 From: lionelllohcd Date: Sun, 19 Feb 2023 23:51:17 -0800 Subject: [PATCH 03/14] Removed trailing spaces to satisfy lint --- src/bootstrap/configure.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 91af36d9070ff..04e798e3949d9 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -382,8 +382,8 @@ def set(key, value): top_level_keys = [] for line in open(rust_dir + '/config.toml.example').read().split("\n"): - if cur_section == None: - if line.count('=') == 1: + if cur_section == None: + if line.count('=') == 1: top_level_key = line.split('=')[0] top_level_key = top_level_key.strip(' #') top_level_keys.append(top_level_key) @@ -465,20 +465,19 @@ def configure_section(lines, config): raise RuntimeError("failed to find config line for {}".format(key)) -def configure_top_level_key(lines, top_level_key, value): - for i, line in enumerate(lines): - if line.startswith('#' + top_level_key + ' = ') or line.startswith(top_level_key + ' = '): +def configure_top_level_key(lines, top_level_key, value): + for i, line in enumerate(lines): + if line.startswith('#' + top_level_key + ' = ') or line.startswith(top_level_key + ' = '): lines[i] = "{} = {}".format(top_level_key, value) - return - + return + raise RuntimeError("failed to find config line for {}".format(top_level_key)) - + for section_key, section_config in config.items(): - if section_key not in sections and section_key not in top_level_keys: + if section_key not in sections and section_key not in top_level_keys: raise RuntimeError("config key {} not in sections or top_level_keys".format(section_key)) - - if section_key in top_level_keys: + if section_key in top_level_keys: configure_top_level_key(sections[None], section_key, section_config) elif section_key == 'target': From fc5db2cd4fc3030fdc4cad0e6e38e7f80a7aa71b Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sat, 4 Feb 2023 15:07:41 -0800 Subject: [PATCH 04/14] Implement -Zlink-directives=yes/no `-Zlink-directives=no` will ignored `#[link]` directives while compiling a crate, so nothing is emitted into the crate's metadata. The assumption is that the build system already knows about the crate's native dependencies and can provide them at link time without these directives. This is another way to address issue # #70093, which is currently addressed by `-Zlink-native-libraries` (implemented in #70095). The latter is implemented at link time, which has the effect of ignoring `#[link]` in *every* crate. This makes it a very large hammer as it requires all native dependencies to be known to the build system to be at all usable, including those in sysroot libraries. I think this means its effectively unused, and definitely under-used. Being able to control this on a crate-by-crate basis should make it much easier to apply when needed. I'm not sure if we need both mechanisms, but we can decide that later. --- compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_metadata/src/native_libs.rs | 7 ++++++- compiler/rustc_session/src/options.rs | 2 ++ tests/rustdoc-ui/z-help.stdout | 1 + .../issues/issue-70093/issue-70093-link-directives.rs | 10 ++++++++++ tests/ui/issues/{ => issue-70093}/issue-70093.rs | 0 6 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/ui/issues/issue-70093/issue-70093-link-directives.rs rename tests/ui/issues/{ => issue-70093}/issue-70093.rs (100%) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index bfb1e822a2697..f9f4c0ec70854 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -756,6 +756,7 @@ fn test_unstable_options_tracking_hash() { tracked!(instrument_coverage, Some(InstrumentCoverage::All)); tracked!(instrument_mcount, true); tracked!(instrument_xray, Some(InstrumentXRay::default())); + tracked!(link_directives, false); tracked!(link_only, true); tracked!(llvm_plugins, vec![String::from("plugin_name")]); tracked!(location_detail, LocationDetail { file: true, line: false, column: false }); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index d823989bb02b8..91cb35f672448 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -103,8 +103,13 @@ impl<'tcx> Collector<'tcx> { } // Process all of the #[link(..)]-style arguments - let sess = &self.tcx.sess; + let sess = self.tcx.sess; let features = self.tcx.features(); + + if !sess.opts.unstable_opts.link_directives { + return; + } + for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) { let Some(items) = m.meta_item_list() else { continue; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 1f3eb8d4832fe..eb8e54fa0bd27 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1489,6 +1489,8 @@ options! { "keep hygiene data after analysis (default: no)"), layout_seed: Option = (None, parse_opt_number, [TRACKED], "seed layout randomization"), + link_directives: bool = (true, parse_bool, [TRACKED], + "honour #[link] directives in crates (default: yes)"), link_native_libraries: bool = (true, parse_bool, [UNTRACKED], "link native libraries in the linker invocation (default: yes)"), link_only: bool = (false, parse_bool, [TRACKED], diff --git a/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout index 58b2f92d15040..55876326e5eab 100644 --- a/tests/rustdoc-ui/z-help.stdout +++ b/tests/rustdoc-ui/z-help.stdout @@ -81,6 +81,7 @@ Multiple options can be combined with commas. -Z keep-hygiene-data=val -- keep hygiene data after analysis (default: no) -Z layout-seed=val -- seed layout randomization + -Z link-directives=val -- honour #[link] directives in crates (default: yes) -Z link-native-libraries=val -- link native libraries in the linker invocation (default: yes) -Z link-only=val -- link the `.rlink` file generated by `-Z no-link` (default: no) -Z llvm-plugins=val -- a list LLVM plugins to enable (space separated) diff --git a/tests/ui/issues/issue-70093/issue-70093-link-directives.rs b/tests/ui/issues/issue-70093/issue-70093-link-directives.rs new file mode 100644 index 0000000000000..83f9b16c4086a --- /dev/null +++ b/tests/ui/issues/issue-70093/issue-70093-link-directives.rs @@ -0,0 +1,10 @@ +// run-pass +// compile-flags: -Zlink-directives=no +// ignore-windows - this will probably only work on unixish systems +// ignore-fuchsia - missing __libc_start_main for some reason (#84733) +// ignore-cross-compile - default-linker-libraries=yes doesn't play well with cross compiling + +#[link(name = "some-random-non-existent-library", kind = "static")] +extern "C" {} + +fn main() {} diff --git a/tests/ui/issues/issue-70093.rs b/tests/ui/issues/issue-70093/issue-70093.rs similarity index 100% rename from tests/ui/issues/issue-70093.rs rename to tests/ui/issues/issue-70093/issue-70093.rs From fde2e40e43b487357fad103beec0ec228b077f61 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 8 Feb 2023 12:05:53 -0800 Subject: [PATCH 05/14] link-directives: clarify usage message --- compiler/rustc_session/src/options.rs | 2 +- tests/rustdoc-ui/z-help.stdout | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index eb8e54fa0bd27..4beac931632be 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1490,7 +1490,7 @@ options! { layout_seed: Option = (None, parse_opt_number, [TRACKED], "seed layout randomization"), link_directives: bool = (true, parse_bool, [TRACKED], - "honour #[link] directives in crates (default: yes)"), + "honor #[link] directives in the compiled crate (default: yes)"), link_native_libraries: bool = (true, parse_bool, [UNTRACKED], "link native libraries in the linker invocation (default: yes)"), link_only: bool = (false, parse_bool, [TRACKED], diff --git a/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout index 55876326e5eab..6aa9785f44e33 100644 --- a/tests/rustdoc-ui/z-help.stdout +++ b/tests/rustdoc-ui/z-help.stdout @@ -81,7 +81,7 @@ Multiple options can be combined with commas. -Z keep-hygiene-data=val -- keep hygiene data after analysis (default: no) -Z layout-seed=val -- seed layout randomization - -Z link-directives=val -- honour #[link] directives in crates (default: yes) + -Z link-directives=val -- honor #[link] directives in the compiled crate (default: yes) -Z link-native-libraries=val -- link native libraries in the linker invocation (default: yes) -Z link-only=val -- link the `.rlink` file generated by `-Z no-link` (default: no) -Z llvm-plugins=val -- a list LLVM plugins to enable (space separated) From 5965948ba12550e03701ebf860d5c10af12f3efc Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 25 Jan 2023 10:47:09 +0000 Subject: [PATCH 06/14] Remove a back compat warning --- compiler/rustc_interface/locales/en-US.ftl | 4 --- compiler/rustc_interface/src/errors.rs | 4 --- compiler/rustc_interface/src/passes.rs | 34 ++++++++-------------- 3 files changed, 12 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_interface/locales/en-US.ftl b/compiler/rustc_interface/locales/en-US.ftl index a7bc0e7af1fe9..da58492ccf27c 100644 --- a/compiler/rustc_interface/locales/en-US.ftl +++ b/compiler/rustc_interface/locales/en-US.ftl @@ -11,10 +11,6 @@ interface_mixed_bin_crate = interface_mixed_proc_macro_crate = cannot mix `proc-macro` crate type with others -interface_proc_macro_doc_without_arg = - Trying to document proc macro crate without passing '--crate-type proc-macro to rustdoc - .warn = The generated documentation may be incorrect - interface_error_writing_dependencies = error writing dependencies to `{$path}`: {$error} diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index 29543fe2f932c..0eedee250269e 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -31,10 +31,6 @@ pub struct MixedBinCrate; #[diag(interface_mixed_proc_macro_crate)] pub struct MixedProcMacroCrate; -#[derive(Diagnostic)] -#[diag(interface_proc_macro_doc_without_arg)] -pub struct ProcMacroDocWithoutArg; - #[derive(Diagnostic)] #[diag(interface_error_writing_dependencies)] pub struct ErrorWritingDependencies<'a> { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index aa59654099a68..81c1d665ef072 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -287,28 +287,18 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>) sess.emit_warning(errors::ProcMacroCratePanicAbort); } - // For backwards compatibility, we don't try to run proc macro injection - // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being - // specified. This should only affect users who manually invoke 'rustdoc', as - // 'cargo doc' will automatically pass the proper '--crate-type' flags. - // However, we do emit a warning, to let such users know that they should - // start passing '--crate-type proc-macro' - if has_proc_macro_decls && sess.opts.actually_rustdoc && !is_proc_macro_crate { - sess.emit_warning(errors::ProcMacroDocWithoutArg); - } else { - krate = sess.time("maybe_create_a_macro_crate", || { - let is_test_crate = sess.opts.test; - rustc_builtin_macros::proc_macro_harness::inject( - sess, - resolver, - krate, - is_proc_macro_crate, - has_proc_macro_decls, - is_test_crate, - sess.diagnostic(), - ) - }); - } + krate = sess.time("maybe_create_a_macro_crate", || { + let is_test_crate = sess.opts.test; + rustc_builtin_macros::proc_macro_harness::inject( + sess, + resolver, + krate, + is_proc_macro_crate, + has_proc_macro_decls, + is_test_crate, + sess.diagnostic(), + ) + }); // Done with macro expansion! From e39fe374df14334be3b5eb081a006fad8e4419f7 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sat, 11 Feb 2023 00:53:19 +0100 Subject: [PATCH 07/14] Add check for invalid \`#[macro_export]\` arguments --- compiler/rustc_lint_defs/src/builtin.rs | 30 ++++++++++++++++++ compiler/rustc_passes/locales/en-US.ftl | 4 +++ compiler/rustc_passes/src/check_attr.rs | 31 +++++++++++++++++-- compiler/rustc_passes/src/errors.rs | 12 +++++-- .../invalid_macro_export_argument.rs | 26 ++++++++++++++++ .../invalid_macro_export_argument.stderr | 16 ++++++++++ 6 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 tests/ui/attributes/invalid_macro_export_argument.rs create mode 100644 tests/ui/attributes/invalid_macro_export_argument.stderr diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9d8ad9d9ed9f6..46ec1a2dca1f7 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4103,3 +4103,33 @@ declare_lint! { }; report_in_external_macro } + +declare_lint! { + /// The `invalid_macro_export_arguments` lint detects cases where `#[macro_export]` is being used with invalid arguments. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(invalid_macro_export_arguments)] + /// + /// #[macro_export(invalid_parameter)] + /// macro_rules! myMacro { + /// () => { + /// // [...] + /// } + /// } + /// + /// #[macro_export(too, many, items)] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The only valid argument is `#[macro_export(local_inner_macros)]` or no argument (`#[macro_export]`). + /// You can't have multiple arguments in a `#[macro_export(..)]`, or mention arguments other than `local_inner_macros`. + /// + pub INVALID_MACRO_EXPORT_ARGUMENTS, + Warn, + "\"invalid_parameter\" isn't a valid argument for `#[macro_export]`", +} diff --git a/compiler/rustc_passes/locales/en-US.ftl b/compiler/rustc_passes/locales/en-US.ftl index 0c7e02912d4ef..789a7cef4ae71 100644 --- a/compiler/rustc_passes/locales/en-US.ftl +++ b/compiler/rustc_passes/locales/en-US.ftl @@ -745,3 +745,7 @@ passes_proc_macro_invalid_abi = proc macro functions may not be `extern "{$abi}" passes_proc_macro_unsafe = proc macro functions may not be `unsafe` passes_skipping_const_checks = skipping const checks + +passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]` argument + +passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bb09dcbdd6980..5ef3e13eff801 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -23,7 +23,8 @@ use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{ParamEnv, TyCtxt}; use rustc_session::lint::builtin::{ - CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES, + CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, + UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Symbol}; @@ -2102,7 +2103,33 @@ impl CheckAttrVisitor<'_> { fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) { if target != Target::MacroDef { - self.tcx.emit_spanned_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::MacroExport); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::MacroExport::Normal, + ); + } else if let Some(meta_item_list) = attr.meta_item_list() && + !meta_item_list.is_empty() { + if meta_item_list.len() > 1 { + self.tcx.emit_spanned_lint( + INVALID_MACRO_EXPORT_ARGUMENTS, + hir_id, + attr.span, + errors::MacroExport::TooManyItems, + ); + } else { + if meta_item_list[0].name_or_empty() != sym::local_inner_macros { + self.tcx.emit_spanned_lint( + INVALID_MACRO_EXPORT_ARGUMENTS, + hir_id, + meta_item_list[0].span(), + errors::MacroExport::UnknownItem { + name: meta_item_list[0].name_or_empty(), + }, + ); + } + } } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 82fc3eeff94ab..2c0d21b479848 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -641,8 +641,16 @@ pub struct MacroUse { } #[derive(LintDiagnostic)] -#[diag(passes_macro_export)] -pub struct MacroExport; +pub enum MacroExport { + #[diag(passes_macro_export)] + Normal, + + #[diag(passes_invalid_macro_export_arguments)] + UnknownItem { name: Symbol }, + + #[diag(passes_invalid_macro_export_arguments_too_many_items)] + TooManyItems, +} #[derive(LintDiagnostic)] #[diag(passes_plugin_registrar)] diff --git a/tests/ui/attributes/invalid_macro_export_argument.rs b/tests/ui/attributes/invalid_macro_export_argument.rs new file mode 100644 index 0000000000000..85d009f11a6f3 --- /dev/null +++ b/tests/ui/attributes/invalid_macro_export_argument.rs @@ -0,0 +1,26 @@ +// check-pass +#[macro_export(hello, world)] //~ WARN `#[macro_export]` can only take 1 or 0 arguments +macro_rules! a { + () => () +} + +#[macro_export(not_local_inner_macros)] //~ WARN `not_local_inner_macros` isn't a valid `#[macro_export]` argument +macro_rules! b { + () => () +} + +#[macro_export] +macro_rules! c { + () => () +} +#[macro_export(local_inner_macros)] +macro_rules! d { + () => () +} + +#[macro_export()] +macro_rules! e { + () => () +} + +fn main() {} diff --git a/tests/ui/attributes/invalid_macro_export_argument.stderr b/tests/ui/attributes/invalid_macro_export_argument.stderr new file mode 100644 index 0000000000000..a4e17642c2aac --- /dev/null +++ b/tests/ui/attributes/invalid_macro_export_argument.stderr @@ -0,0 +1,16 @@ +warning: `#[macro_export]` can only take 1 or 0 arguments + --> $DIR/invalid_macro_export_argument.rs:2:1 + | +LL | #[macro_export(hello, world)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(invalid_macro_export_arguments)]` on by default + +warning: `not_local_inner_macros` isn't a valid `#[macro_export]` argument + --> $DIR/invalid_macro_export_argument.rs:7:16 + | +LL | #[macro_export(not_local_inner_macros)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: 2 warnings emitted + From 60b0da1541b82eca0acee742ef816462d3cb1a0d Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 9 Feb 2023 15:00:32 +0000 Subject: [PATCH 08/14] Test rustdoc encountering `proc_macro_derive` in a non-proc-macro crate --- tests/rustdoc-ui/proc_macro_bug.rs | 12 ++++++++++++ tests/rustdoc-ui/proc_macro_bug.stderr | 8 ++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/rustdoc-ui/proc_macro_bug.rs create mode 100644 tests/rustdoc-ui/proc_macro_bug.stderr diff --git a/tests/rustdoc-ui/proc_macro_bug.rs b/tests/rustdoc-ui/proc_macro_bug.rs new file mode 100644 index 0000000000000..e384e4863adb9 --- /dev/null +++ b/tests/rustdoc-ui/proc_macro_bug.rs @@ -0,0 +1,12 @@ +// regression test for failing to pass `--crate-type proc-macro` to rustdoc +// when documenting a proc macro crate https://github.com/rust-lang/rust/pull/107291 + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_derive(DeriveA)] +//~^ ERROR the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type +pub fn a_derive(input: TokenStream) -> TokenStream { + input +} diff --git a/tests/rustdoc-ui/proc_macro_bug.stderr b/tests/rustdoc-ui/proc_macro_bug.stderr new file mode 100644 index 0000000000000..5b048097c4964 --- /dev/null +++ b/tests/rustdoc-ui/proc_macro_bug.stderr @@ -0,0 +1,8 @@ +error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type + --> $DIR/proc_macro_bug.rs:8:1 + | +LL | #[proc_macro_derive(DeriveA)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + From 98525aeee7b1f0ddf3573c2921a0d1ef914774a8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 22 Feb 2023 01:11:57 +0000 Subject: [PATCH 09/14] Check object's supertrait and associated type bounds in new solver --- .../src/solve/assembly.rs | 11 +++- .../src/solve/project_goals.rs | 44 +++++++++++++ .../src/solve/trait_goals.rs | 39 ++++++++++++ .../solve/trait_goals/structural_traits.rs | 62 ++++++++++++++++++- tests/ui/traits/new-solver/object-unsafety.rs | 20 ++++++ .../traits/new-solver/object-unsafety.stderr | 19 ++++++ 6 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/new-solver/object-unsafety.rs create mode 100644 tests/ui/traits/new-solver/object-unsafety.stderr diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 6172a9539f64d..dec9f8016b0c1 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -99,6 +99,15 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable> + Copy + Eq { requirements: impl IntoIterator>>, ) -> QueryResult<'tcx>; + // Consider a clause specifically for a `dyn Trait` self type. This requires + // additionally checking all of the supertraits and object bounds to hold, + // since they're not implied by the well-formedness of the object type. + fn consider_object_bound_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + ) -> QueryResult<'tcx>; + fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -455,7 +464,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { for assumption in elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty))) { - match G::consider_implied_clause(self, goal, assumption.predicate, []) { + match G::consider_object_bound_candidate(self, goal, assumption.predicate) { Ok(result) => { candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index d4fdd54573751..807527e866002 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -128,6 +128,50 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { } } + fn consider_object_bound_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + ) -> QueryResult<'tcx> { + if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() + && poly_projection_pred.projection_def_id() == goal.predicate.def_id() + { + ecx.probe(|ecx| { + let assumption_projection_pred = + ecx.instantiate_binder_with_infer(poly_projection_pred); + let mut nested_goals = ecx.eq( + goal.param_env, + goal.predicate.projection_ty, + assumption_projection_pred.projection_ty, + )?; + + let tcx = ecx.tcx(); + let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { + bug!("expected object type in `consider_object_bound_candidate`"); + }; + nested_goals.extend( + structural_traits::predicates_for_object_candidate( + tcx, + goal.predicate.projection_ty.trait_ref(tcx), + bounds, + ) + .into_iter() + .map(|pred| goal.with(tcx, pred)), + ); + + let subst_certainty = ecx.evaluate_all(nested_goals)?; + + ecx.eq_term_and_make_canonical_response( + goal, + subst_certainty, + assumption_projection_pred.term, + ) + }) + } else { + Err(NoSolution) + } + } + fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index aff79739d45ff..9aded080f20ba 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -86,6 +86,45 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } } + fn consider_object_bound_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + ) -> QueryResult<'tcx> { + if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() + && poly_trait_pred.def_id() == goal.predicate.def_id() + { + // FIXME: Constness and polarity + ecx.probe(|ecx| { + let assumption_trait_pred = + ecx.instantiate_binder_with_infer(poly_trait_pred); + let mut nested_goals = ecx.eq( + goal.param_env, + goal.predicate.trait_ref, + assumption_trait_pred.trait_ref, + )?; + + let tcx = ecx.tcx(); + let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { + bug!("expected object type in `consider_object_bound_candidate`"); + }; + nested_goals.extend( + structural_traits::predicates_for_object_candidate( + tcx, + goal.predicate.trait_ref, + bounds, + ) + .into_iter() + .map(|pred| goal.with(tcx, pred)), + ); + + ecx.evaluate_all_and_make_canonical_response(nested_goals) + }) + } else { + Err(NoSolution) + } + } + fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs index 2c13465d347c4..3fcf96be203ca 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs @@ -1,6 +1,7 @@ +use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Movability, Mutability}; use rustc_infer::traits::query::NoSolution; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable}; use crate::solve::EvalCtxt; @@ -233,3 +234,62 @@ pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>( } } } + +pub(crate) fn predicates_for_object_candidate<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + object_bound: &'tcx ty::List>, +) -> Vec> { + let mut requirements = vec![]; + requirements.extend( + tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.substs).predicates, + ); + for item in tcx.associated_items(trait_ref.def_id).in_definition_order() { + if item.kind == ty::AssocKind::Type { + requirements.extend(tcx.item_bounds(item.def_id).subst(tcx, trait_ref.substs)); + } + } + + let mut replace_projection_with = FxHashMap::default(); + for bound in object_bound { + let bound = bound.no_bound_vars().expect("higher-ranked projections not supported, yet"); + if let ty::ExistentialPredicate::Projection(proj) = bound { + let proj = proj.with_self_ty(tcx, trait_ref.self_ty()); + let old_ty = replace_projection_with.insert( + proj.projection_ty, + proj.term.ty().expect("expected only types in dyn right now"), + ); + assert_eq!( + old_ty, + None, + "{} has two substitutions: {} and {}", + proj.projection_ty, + proj.term, + old_ty.unwrap() + ); + } + } + + requirements.fold_with(&mut ReplaceProjectionWith { tcx, mapping: replace_projection_with }) +} + +struct ReplaceProjectionWith<'tcx> { + tcx: TyCtxt<'tcx>, + mapping: FxHashMap, Ty<'tcx>>, +} + +impl<'tcx> TypeFolder> for ReplaceProjectionWith<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Alias(ty::Projection, alias_ty) = *ty.kind() + && let Some(replacement) = self.mapping.get(&alias_ty) + { + *replacement + } else { + ty.super_fold_with(self) + } + } +} diff --git a/tests/ui/traits/new-solver/object-unsafety.rs b/tests/ui/traits/new-solver/object-unsafety.rs new file mode 100644 index 0000000000000..7bdd863a762c4 --- /dev/null +++ b/tests/ui/traits/new-solver/object-unsafety.rs @@ -0,0 +1,20 @@ +// compile-flags: -Ztrait-solver=next + +trait Setup { + type From: Copy; +} + +fn copy(from: &U::From) -> U::From { + *from +} + +pub fn copy_any(t: &T) -> T { + copy::>(t) + //~^ ERROR the trait bound `dyn Setup: Setup` is not satisfied +} + +fn main() { + let x = String::from("Hello, world"); + let y = copy_any(&x); + println!("{y}"); +} diff --git a/tests/ui/traits/new-solver/object-unsafety.stderr b/tests/ui/traits/new-solver/object-unsafety.stderr new file mode 100644 index 0000000000000..198ac623df8a6 --- /dev/null +++ b/tests/ui/traits/new-solver/object-unsafety.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `dyn Setup: Setup` is not satisfied + --> $DIR/object-unsafety.rs:12:12 + | +LL | copy::>(t) + | ^^^^^^^^^^^^^^^^^ the trait `Setup` is not implemented for `dyn Setup` + | +note: required by a bound in `copy` + --> $DIR/object-unsafety.rs:7:12 + | +LL | fn copy(from: &U::From) -> U::From { + | ^^^^^ required by this bound in `copy` +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | pub fn copy_any(t: &T) -> T where dyn Setup: Setup { + | ++++++++++++++++++++++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 2540c2b76138b988629b48f54381835f13f18792 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 22 Feb 2023 01:26:01 +0000 Subject: [PATCH 10/14] Make higher-ranked projections in object types work in new solver --- .../src/solve/project_goals.rs | 3 +- .../src/solve/trait_goals.rs | 3 +- .../solve/trait_goals/structural_traits.rs | 43 ++++++++++++------- .../new-solver/higher-ranked-dyn-bounds.rs | 17 ++++++++ 4 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 tests/ui/traits/new-solver/higher-ranked-dyn-bounds.rs diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 807527e866002..88fd8bb8bd095 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -151,7 +151,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { }; nested_goals.extend( structural_traits::predicates_for_object_candidate( - tcx, + ecx, + goal.param_env, goal.predicate.projection_ty.trait_ref(tcx), bounds, ) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 9aded080f20ba..6048082228b35 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -110,7 +110,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }; nested_goals.extend( structural_traits::predicates_for_object_candidate( - tcx, + ecx, + goal.param_env, goal.predicate.trait_ref, bounds, ) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs index 3fcf96be203ca..bd4950d8067cf 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{Movability, Mutability}; +use rustc_hir::{def_id::DefId, Movability, Mutability}; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable}; @@ -236,10 +236,12 @@ pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>( } pub(crate) fn predicates_for_object_candidate<'tcx>( - tcx: TyCtxt<'tcx>, + ecx: &EvalCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, trait_ref: ty::TraitRef<'tcx>, object_bound: &'tcx ty::List>, ) -> Vec> { + let tcx = ecx.tcx(); let mut requirements = vec![]; requirements.extend( tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.substs).predicates, @@ -252,13 +254,9 @@ pub(crate) fn predicates_for_object_candidate<'tcx>( let mut replace_projection_with = FxHashMap::default(); for bound in object_bound { - let bound = bound.no_bound_vars().expect("higher-ranked projections not supported, yet"); - if let ty::ExistentialPredicate::Projection(proj) = bound { + if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() { let proj = proj.with_self_ty(tcx, trait_ref.self_ty()); - let old_ty = replace_projection_with.insert( - proj.projection_ty, - proj.term.ty().expect("expected only types in dyn right now"), - ); + let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj)); assert_eq!( old_ty, None, @@ -270,24 +268,37 @@ pub(crate) fn predicates_for_object_candidate<'tcx>( } } - requirements.fold_with(&mut ReplaceProjectionWith { tcx, mapping: replace_projection_with }) + requirements.fold_with(&mut ReplaceProjectionWith { + ecx, + param_env, + mapping: replace_projection_with, + }) } -struct ReplaceProjectionWith<'tcx> { - tcx: TyCtxt<'tcx>, - mapping: FxHashMap, Ty<'tcx>>, +struct ReplaceProjectionWith<'a, 'tcx> { + ecx: &'a EvalCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + mapping: FxHashMap>, } -impl<'tcx> TypeFolder> for ReplaceProjectionWith<'tcx> { +impl<'tcx> TypeFolder> for ReplaceProjectionWith<'_, 'tcx> { fn interner(&self) -> TyCtxt<'tcx> { - self.tcx + self.ecx.tcx() } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { if let ty::Alias(ty::Projection, alias_ty) = *ty.kind() - && let Some(replacement) = self.mapping.get(&alias_ty) + && let Some(replacement) = self.mapping.get(&alias_ty.def_id) { - *replacement + let proj = self.ecx.instantiate_binder_with_infer(*replacement); + // Technically this folder could be fallible? + let nested = self + .ecx + .eq(self.param_env, alias_ty, proj.projection_ty) + .expect("expected to be able to unify goal projection with dyn's projection"); + // Technically we could register these too.. + assert!(nested.is_empty(), "did not expect unification to have any nested goals"); + proj.term.ty().unwrap() } else { ty.super_fold_with(self) } diff --git a/tests/ui/traits/new-solver/higher-ranked-dyn-bounds.rs b/tests/ui/traits/new-solver/higher-ranked-dyn-bounds.rs new file mode 100644 index 0000000000000..c886aeeda3e46 --- /dev/null +++ b/tests/ui/traits/new-solver/higher-ranked-dyn-bounds.rs @@ -0,0 +1,17 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +trait Trait<'a> { + type Item: for<'b> Trait2<'b>; +} + +trait Trait2<'a> {} +impl Trait2<'_> for () {} + +fn needs_trait(_: Box Trait<'a> + ?Sized>) {} + +fn foo(x: Box Trait<'a, Item = ()>>) { + needs_trait(x); +} + +fn main() {} From ed30efff3b2d9e4636d3b1b9e9dc275362b64a62 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 22 Feb 2023 01:39:41 +0000 Subject: [PATCH 11/14] Comments, another test --- .../solve/trait_goals/structural_traits.rs | 43 ++++++++++++++++++- .../ui/traits/new-solver/more-object-bound.rs | 27 ++++++++++++ .../new-solver/more-object-bound.stderr | 19 ++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/new-solver/more-object-bound.rs create mode 100644 tests/ui/traits/new-solver/more-object-bound.stderr diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs index bd4950d8067cf..9b6cce86fffc4 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs @@ -235,6 +235,40 @@ pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>( } } +/// Assemble a list of predicates that would be present on a theoretical +/// user impl for an object type. These predicates must be checked any time +/// we assemble a built-in object candidate for an object type, since they +/// are not implied by the well-formedness of the type. +/// +/// For example, given the following traits: +/// +/// ```rust,ignore (theoretical code) +/// trait Foo: Baz { +/// type Bar: Copy; +/// } +/// +/// trait Baz {} +/// ``` +/// +/// For the dyn type `dyn Foo`, we can imagine there being a +/// pair of theoretical impls: +/// +/// ```rust,ignore (theoretical code) +/// impl Foo for dyn Foo +/// where +/// Self: Baz, +/// ::Bar: Copy, +/// { +/// type Bar = Ty; +/// } +/// +/// impl Baz for dyn Foo {} +/// ``` +/// +/// However, in order to make such impls well-formed, we need to do an +/// additional step of eagerly folding the associated types in the where +/// clauses of the impl. In this example, that means replacing +/// `::Bar` with `Ty` in the first impl. pub(crate) fn predicates_for_object_candidate<'tcx>( ecx: &EvalCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -247,6 +281,8 @@ pub(crate) fn predicates_for_object_candidate<'tcx>( tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.substs).predicates, ); for item in tcx.associated_items(trait_ref.def_id).in_definition_order() { + // FIXME(associated_const_equality): Also add associated consts to + // the requirements here. if item.kind == ty::AssocKind::Type { requirements.extend(tcx.item_bounds(item.def_id).subst(tcx, trait_ref.substs)); } @@ -290,13 +326,16 @@ impl<'tcx> TypeFolder> for ReplaceProjectionWith<'_, 'tcx> { if let ty::Alias(ty::Projection, alias_ty) = *ty.kind() && let Some(replacement) = self.mapping.get(&alias_ty.def_id) { + // We may have a case where our object type's projection bound is higher-ranked, + // but the where clauses we instantiated are not. We can solve this by instantiating + // the binder at the usage site. let proj = self.ecx.instantiate_binder_with_infer(*replacement); - // Technically this folder could be fallible? + // FIXME: Technically this folder could be fallible? let nested = self .ecx .eq(self.param_env, alias_ty, proj.projection_ty) .expect("expected to be able to unify goal projection with dyn's projection"); - // Technically we could register these too.. + // FIXME: Technically we could register these too.. assert!(nested.is_empty(), "did not expect unification to have any nested goals"); proj.term.ty().unwrap() } else { diff --git a/tests/ui/traits/new-solver/more-object-bound.rs b/tests/ui/traits/new-solver/more-object-bound.rs new file mode 100644 index 0000000000000..712759ef0e612 --- /dev/null +++ b/tests/ui/traits/new-solver/more-object-bound.rs @@ -0,0 +1,27 @@ +// compile-flags: -Ztrait-solver=next +// From #80800 + +trait SuperTrait { + type A; + type B; +} + +trait Trait: SuperTrait::B> {} + +fn transmute(x: A) -> B { + foo::>(x) + //~^ ERROR type annotations needed: cannot satisfy `dyn Trait: Trait` +} + +fn foo(x: T::A) -> B +where + T: Trait, +{ + x +} + +static X: u8 = 0; +fn main() { + let x = transmute::<&u8, &[u8; 1_000_000]>(&X); + println!("{:?}", x[100_000]); +} diff --git a/tests/ui/traits/new-solver/more-object-bound.stderr b/tests/ui/traits/new-solver/more-object-bound.stderr new file mode 100644 index 0000000000000..208fdecb08fc9 --- /dev/null +++ b/tests/ui/traits/new-solver/more-object-bound.stderr @@ -0,0 +1,19 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn Trait: Trait` + --> $DIR/more-object-bound.rs:12:5 + | +LL | foo::>(x) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: cannot satisfy `dyn Trait: Trait` +note: required by a bound in `foo` + --> $DIR/more-object-bound.rs:18:8 + | +LL | fn foo(x: T::A) -> B + | --- required by a bound in this function +LL | where +LL | T: Trait, + | ^^^^^^^^^^^^ required by this bound in `foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0283`. From 34813e205157f0235e5cf8847079f13ac6acd078 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 15 Dec 2022 14:49:11 +0800 Subject: [PATCH 12/14] Test that the compiler/library builds with -Zmir-opt-level=3 -Zvalidate-mir --- config.toml.example | 3 +++ src/bootstrap/builder.rs | 7 +++++++ src/bootstrap/config.rs | 3 +++ src/ci/run.sh | 1 + 4 files changed, 14 insertions(+) diff --git a/config.toml.example b/config.toml.example index df4478bb0cb85..69eb228a2d5a0 100644 --- a/config.toml.example +++ b/config.toml.example @@ -666,6 +666,9 @@ changelog-seen = 2 # LTO entirely. #lto = "thin-local" +# Build compiler with the optimization enabled and -Zvalidate-mir, currently only for `std` +#validate-mir-opts = 3 + # ============================================================================= # Options for specific targets # diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 952c70cec1c05..fe92ee3c18ef4 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1915,6 +1915,13 @@ impl<'a> Builder<'a> { } } + if matches!(mode, Mode::Std) { + if let Some(mir_opt_level) = self.config.rust_validate_mir_opts { + rustflags.arg("-Zvalidate-mir"); + rustflags.arg(&format!("-Zmir-opt-level={}", mir_opt_level)); + } + } + Cargo { command: cargo, rustflags, rustdocflags, allow_features } } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 56f96734bbbc7..4a563bc396dc4 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -173,6 +173,7 @@ pub struct Config { pub rust_profile_use: Option, pub rust_profile_generate: Option, pub rust_lto: RustcLto, + pub rust_validate_mir_opts: Option, pub llvm_profile_use: Option, pub llvm_profile_generate: bool, pub llvm_libunwind_default: Option, @@ -770,6 +771,7 @@ define_config! { // ignored; this is set from an env var set by bootstrap.py download_rustc: Option = "download-rustc", lto: Option = "lto", + validate_mir_opts: Option = "validate-mir-opts", } } @@ -1149,6 +1151,7 @@ impl Config { .as_deref() .map(|value| RustcLto::from_str(value).unwrap()) .unwrap_or_default(); + config.rust_validate_mir_opts = rust.validate_mir_opts; } else { config.rust_profile_use = flags.rust_profile_use; config.rust_profile_generate = flags.rust_profile_generate; diff --git a/src/ci/run.sh b/src/ci/run.sh index 93dccb54c4e38..1f451fc479c24 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -58,6 +58,7 @@ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-manage-submodules" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-locked-deps" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-cargo-native-static" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-units-std=1" +RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.validate-mir-opts=3" # Only produce xz tarballs on CI. gz tarballs will be generated by the release # process by recompressing the existing xz ones. This decreases the storage From 15813cf6466ed4d7d546b2f08e9521dfe496ef53 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 28 Dec 2022 21:10:57 +0800 Subject: [PATCH 13/14] enable validate-mir-opts in mingw-check --- src/ci/docker/host-x86_64/mingw-check/Dockerfile | 4 +++- src/ci/run.sh | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index 4cc5d9f8a0daf..a7220710830f6 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -23,6 +23,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN curl -sL https://nodejs.org/dist/v16.9.0/node-v16.9.0-linux-x64.tar.xz | tar -xJ ENV PATH="/node-v16.9.0-linux-x64/bin:${PATH}" +ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3" + # Install es-check # Pin its version to prevent unrelated CI failures due to future es-check versions. RUN npm install es-check@6.1.1 eslint@8.6.0 -g @@ -39,7 +41,7 @@ COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/ ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu --all-targets && \ - python3 ../x.py build --stage 0 src/tools/build-manifest && \ + python3 ../x.py build --stage 0 src/tools/build-manifest library/std && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ python3 ../x.py test --stage 0 core alloc std test proc_macro && \ # Build both public and internal documentation. diff --git a/src/ci/run.sh b/src/ci/run.sh index 1f451fc479c24..93dccb54c4e38 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -58,7 +58,6 @@ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-manage-submodules" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-locked-deps" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-cargo-native-static" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-units-std=1" -RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.validate-mir-opts=3" # Only produce xz tarballs on CI. gz tarballs will be generated by the release # process by recompressing the existing xz ones. This decreases the storage From 001bceeb1ecd1239031a5c7ff711106b2b1c54cc Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 24 Feb 2023 22:51:25 +0000 Subject: [PATCH 14/14] check is default to all targets now --- src/ci/docker/host-x86_64/mingw-check/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index a7220710830f6..98bd90210d615 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -40,8 +40,8 @@ COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/ ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ - python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu --all-targets && \ - python3 ../x.py build --stage 0 src/tools/build-manifest library/std && \ + python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ + python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ python3 ../x.py test --stage 0 core alloc std test proc_macro && \ # Build both public and internal documentation.