diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index 28000916e0c30..a7a708fe7dec3 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -68,7 +68,6 @@ fn frame_pointer_r11( _arch: InlineAsmArch, has_feature: impl FnMut(&str) -> bool, target: &Target, - _allocating: bool, ) -> Result<(), &'static str> { if !frame_pointer_is_r7(has_feature, target) { Err("the frame pointer (r11) cannot be used as an operand for inline asm") @@ -81,7 +80,6 @@ fn frame_pointer_r7( _arch: InlineAsmArch, has_feature: impl FnMut(&str) -> bool, target: &Target, - _allocating: bool, ) -> Result<(), &'static str> { if frame_pointer_is_r7(has_feature, target) { Err("the frame pointer (r7) cannot be used as an operand for inline asm") diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index a09c87b3ec2b2..e2268a61a4257 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -90,7 +90,7 @@ macro_rules! def_regs { match name { $( $($alias)|* | $reg_name => { - $($filter(_arch, &mut _has_feature, _target, false)?;)? + $($filter(_arch, &mut _has_feature, _target)?;)? Ok(Self::$reg) } )* @@ -114,7 +114,7 @@ macro_rules! def_regs { #[allow(unused_imports)] use super::{InlineAsmReg, InlineAsmRegClass}; $( - if $($filter(_arch, &mut _has_feature, _target, true).is_ok() &&)? true { + if $($filter(_arch, &mut _has_feature, _target).is_ok() &&)? true { if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { set.insert(InlineAsmReg::$arch($arch_reg::$reg)); } diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index ced7483b00571..185d6ac8246c9 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -52,7 +52,6 @@ fn not_e( _arch: InlineAsmArch, mut has_feature: impl FnMut(&str) -> bool, _target: &Target, - _allocating: bool, ) -> Result<(), &'static str> { if has_feature("e") { Err("register can't be used with the `e` target feature") diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 0f62c19e1a3cd..90660dad4c2a1 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -133,7 +133,6 @@ fn x86_64_only( arch: InlineAsmArch, _has_feature: impl FnMut(&str) -> bool, _target: &Target, - _allocating: bool, ) -> Result<(), &'static str> { match arch { InlineAsmArch::X86 => Err("register is only available on x86_64"), @@ -146,13 +145,9 @@ fn high_byte( arch: InlineAsmArch, _has_feature: impl FnMut(&str) -> bool, _target: &Target, - allocating: bool, ) -> Result<(), &'static str> { match arch { - InlineAsmArch::X86_64 if allocating => { - // The error message isn't actually used... - Err("high byte registers are not allocated by reg_byte") - } + InlineAsmArch::X86_64 => Err("high byte registers cannot be used as an operand on x86_64"), _ => Ok(()), } } diff --git a/config.toml.example b/config.toml.example index d8b550d4c7423..b78e2b7b746c4 100644 --- a/config.toml.example +++ b/config.toml.example @@ -373,7 +373,9 @@ changelog-seen = 2 # Whether to download the stage 1 and 2 compilers from CI. # This is mostly useful for tools; if you have changes to `compiler/` they will be ignored. # -# FIXME: currently, this also uses the downloaded compiler for stage0, but that causes unnecessary rebuilds. +# You can set this to "if-unchanged" to only download if `compiler/` has not been modified. +# +# FIXME(#82739): currently, this also uses the downloaded compiler for stage0, but that causes unnecessary rebuilds. #download-rustc = false # Number of codegen units to use for each compiler invocation. A value of 0 diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index 7f8c33dac561f..da2415e361077 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -993,6 +993,7 @@ impl Ord for Ipv4Addr { } impl IntoInner for Ipv4Addr { + #[inline] fn into_inner(self) -> c::in_addr { self.inner } @@ -1800,11 +1801,13 @@ impl Ord for Ipv6Addr { } impl AsInner for Ipv6Addr { + #[inline] fn as_inner(&self) -> &c::in6_addr { &self.inner } } impl FromInner for Ipv6Addr { + #[inline] fn from_inner(addr: c::in6_addr) -> Ipv6Addr { Ipv6Addr { inner: addr } } diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 075756b73ba82..23af00d62930a 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -383,7 +383,7 @@ def __init__(self): self.nix_deps_dir = None self.rustc_commit = None - def download_stage0(self): + def download_toolchain(self, stage0=True, rustc_channel=None): """Fetch the build system for Rust, written in Rust This method will build a cache directory, then it will fetch the @@ -393,43 +393,47 @@ def download_stage0(self): Each downloaded tarball is extracted, after that, the script will move all the content to the right place. """ - rustc_channel = self.rustc_channel + if rustc_channel is None: + rustc_channel = self.rustc_channel rustfmt_channel = self.rustfmt_channel - - if self.rustc().startswith(self.bin_root()) and \ - (not os.path.exists(self.rustc()) or - self.program_out_of_date(self.rustc_stamp(), self.date + str(self.rustc_commit))): - if os.path.exists(self.bin_root()): - shutil.rmtree(self.bin_root()) - download_rustc = self.rustc_commit is not None + bin_root = self.bin_root(stage0) + + key = self.date + if not stage0: + key += str(self.rustc_commit) + if self.rustc(stage0).startswith(bin_root) and \ + (not os.path.exists(self.rustc(stage0)) or + self.program_out_of_date(self.rustc_stamp(stage0), key)): + if os.path.exists(bin_root): + shutil.rmtree(bin_root) tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' filename = "rust-std-{}-{}{}".format( rustc_channel, self.build, tarball_suffix) pattern = "rust-std-{}".format(self.build) - self._download_component_helper(filename, pattern, tarball_suffix, download_rustc) + self._download_component_helper(filename, pattern, tarball_suffix, stage0) filename = "rustc-{}-{}{}".format(rustc_channel, self.build, tarball_suffix) - self._download_component_helper(filename, "rustc", tarball_suffix, download_rustc) + self._download_component_helper(filename, "rustc", tarball_suffix, stage0) filename = "cargo-{}-{}{}".format(rustc_channel, self.build, tarball_suffix) self._download_component_helper(filename, "cargo", tarball_suffix) - if self.rustc_commit is not None: + if not stage0: filename = "rustc-dev-{}-{}{}".format(rustc_channel, self.build, tarball_suffix) self._download_component_helper( - filename, "rustc-dev", tarball_suffix, download_rustc + filename, "rustc-dev", tarball_suffix, stage0 ) - self.fix_bin_or_dylib("{}/bin/rustc".format(self.bin_root())) - self.fix_bin_or_dylib("{}/bin/rustdoc".format(self.bin_root())) - self.fix_bin_or_dylib("{}/bin/cargo".format(self.bin_root())) - lib_dir = "{}/lib".format(self.bin_root()) + self.fix_bin_or_dylib("{}/bin/rustc".format(bin_root)) + self.fix_bin_or_dylib("{}/bin/rustdoc".format(bin_root)) + self.fix_bin_or_dylib("{}/bin/cargo".format(bin_root)) + lib_dir = "{}/lib".format(bin_root) for lib in os.listdir(lib_dir): if lib.endswith(".so"): self.fix_bin_or_dylib(os.path.join(lib_dir, lib), rpath_libz=True) - with output(self.rustc_stamp()) as rust_stamp: - rust_stamp.write(self.date + str(self.rustc_commit)) + with output(self.rustc_stamp(stage0)) as rust_stamp: + rust_stamp.write(key) - if self.rustfmt() and self.rustfmt().startswith(self.bin_root()) and ( + if self.rustfmt() and self.rustfmt().startswith(bin_root) and ( not os.path.exists(self.rustfmt()) or self.program_out_of_date(self.rustfmt_stamp(), self.rustfmt_channel) ): @@ -440,12 +444,13 @@ def download_stage0(self): self._download_component_helper( filename, "rustfmt-preview", tarball_suffix, key=date ) - self.fix_bin_or_dylib("{}/bin/rustfmt".format(self.bin_root())) - self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(self.bin_root())) + self.fix_bin_or_dylib("{}/bin/rustfmt".format(bin_root)) + self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(bin_root)) with output(self.rustfmt_stamp()) as rustfmt_stamp: rustfmt_stamp.write(self.rustfmt_channel) - if self.downloading_llvm(): + # Avoid downloading LLVM twice (once for stage0 and once for the master rustc) + if self.downloading_llvm() and stage0: # We want the most recent LLVM submodule update to avoid downloading # LLVM more often than necessary. # @@ -498,27 +503,26 @@ def downloading_llvm(self): or (opt == "if-available" and self.build in supported_platforms) def _download_component_helper( - self, filename, pattern, tarball_suffix, download_rustc=False, key=None + self, filename, pattern, tarball_suffix, stage0=True, key=None ): if key is None: - if download_rustc: - key = self.rustc_commit - else: + if stage0: key = self.date + else: + key = self.rustc_commit cache_dst = os.path.join(self.build_dir, "cache") rustc_cache = os.path.join(cache_dst, key) if not os.path.exists(rustc_cache): os.makedirs(rustc_cache) - if download_rustc: - url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(self.rustc_commit) - else: + if stage0: url = "{}/dist/{}".format(self._download_url, key) + else: + url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(self.rustc_commit) tarball = os.path.join(rustc_cache, filename) if not os.path.exists(tarball): - do_verify = not download_rustc - get("{}/{}".format(url, filename), tarball, verbose=self.verbose, do_verify=do_verify) - unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose) + get("{}/{}".format(url, filename), tarball, verbose=self.verbose, do_verify=stage0) + unpack(tarball, tarball_suffix, self.bin_root(stage0), match=pattern, verbose=self.verbose) def _download_ci_llvm(self, llvm_sha, llvm_assertions): cache_prefix = "llvm-{}-{}".format(llvm_sha, llvm_assertions) @@ -576,10 +580,10 @@ def fix_bin_or_dylib(self, fname, rpath_libz=False): nix_os_msg = "info: you seem to be running NixOS. Attempting to patch" print(nix_os_msg, fname) - # Only build `stage0/.nix-deps` once. + # Only build `.nix-deps` once. nix_deps_dir = self.nix_deps_dir if not nix_deps_dir: - nix_deps_dir = "{}/.nix-deps".format(self.bin_root()) + nix_deps_dir = ".nix-deps" if not os.path.exists(nix_deps_dir): os.makedirs(nix_deps_dir) @@ -637,11 +641,13 @@ def fix_bin_or_dylib(self, fname, rpath_libz=False): print("warning: failed to call patchelf:", reason) return - # Return the stage1 compiler to download, if any. - def maybe_download_rustc(self): + # If `download-rustc` is set, download the most recent commit with CI artifacts + def maybe_download_ci_toolchain(self): # If `download-rustc` is not set, default to rebuilding. - if self.get_toml("download-rustc", section="rust") != "true": + download_rustc = self.get_toml("download-rustc", section="rust") + if download_rustc is None or download_rustc == "false": return None + assert download_rustc == "true" or download_rustc == "if-unchanged", download_rustc # Handle running from a directory other than the top level rev_parse = ["git", "rev-parse", "--show-toplevel"] @@ -656,19 +662,27 @@ def maybe_download_rustc(self): # Warn if there were changes to the compiler since the ancestor commit. status = subprocess.call(["git", "diff-index", "--quiet", commit, "--", compiler]) if status != 0: + if download_rustc == "if-unchanged": + return None print("warning: `download-rustc` is enabled, but there are changes to compiler/") - return commit + if self.verbose: + print("using downloaded stage1 artifacts from CI (commit {})".format(commit)) + self.rustc_commit = commit + # FIXME: support downloading artifacts from the beta channel + self.download_toolchain(False, "nightly") - def rustc_stamp(self): - """Return the path for .rustc-stamp + def rustc_stamp(self, stage0): + """Return the path for .rustc-stamp at the given stage >>> rb = RustBuild() >>> rb.build_dir = "build" - >>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp") + >>> rb.rustc_stamp(True) == os.path.join("build", "stage0", ".rustc-stamp") + True + >>> rb.rustc_stamp(False) == os.path.join("build", "ci-rustc", ".rustc-stamp") True """ - return os.path.join(self.bin_root(), '.rustc-stamp') + return os.path.join(self.bin_root(stage0), '.rustc-stamp') def rustfmt_stamp(self): """Return the path for .rustfmt-stamp @@ -678,7 +692,7 @@ def rustfmt_stamp(self): >>> rb.rustfmt_stamp() == os.path.join("build", "stage0", ".rustfmt-stamp") True """ - return os.path.join(self.bin_root(), '.rustfmt-stamp') + return os.path.join(self.bin_root(True), '.rustfmt-stamp') def llvm_stamp(self): """Return the path for .rustfmt-stamp @@ -698,21 +712,27 @@ def program_out_of_date(self, stamp_path, key): with open(stamp_path, 'r') as stamp: return key != stamp.read() - def bin_root(self): - """Return the binary root directory + def bin_root(self, stage0): + """Return the binary root directory for the given stage >>> rb = RustBuild() >>> rb.build_dir = "build" - >>> rb.bin_root() == os.path.join("build", "stage0") + >>> rb.bin_root(True) == os.path.join("build", "stage0") + True + >>> rb.bin_root(False) == os.path.join("build", "ci-rustc") True When the 'build' property is given should be a nested directory: >>> rb.build = "devel" - >>> rb.bin_root() == os.path.join("build", "devel", "stage0") + >>> rb.bin_root(True) == os.path.join("build", "devel", "stage0") True """ - return os.path.join(self.build_dir, self.build, "stage0") + if stage0: + subdir = "stage0" + else: + subdir = "ci-rustc" + return os.path.join(self.build_dir, self.build, subdir) def llvm_root(self): """Return the CI LLVM root directory @@ -775,9 +795,9 @@ def cargo(self): """Return config path for cargo""" return self.program_config('cargo') - def rustc(self): + def rustc(self, stage0): """Return config path for rustc""" - return self.program_config('rustc') + return self.program_config('rustc', stage0) def rustfmt(self): """Return config path for rustfmt""" @@ -785,23 +805,27 @@ def rustfmt(self): return None return self.program_config('rustfmt') - def program_config(self, program): - """Return config path for the given program + def program_config(self, program, stage0=True): + """Return config path for the given program at the given stage >>> rb = RustBuild() >>> rb.config_toml = 'rustc = "rustc"\\n' >>> rb.program_config('rustc') 'rustc' >>> rb.config_toml = '' - >>> cargo_path = rb.program_config('cargo') - >>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(), + >>> cargo_path = rb.program_config('cargo', True) + >>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(True), + ... "bin", "cargo") + True + >>> cargo_path = rb.program_config('cargo', False) + >>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(False), ... "bin", "cargo") True """ config = self.get_toml(program) if config: return os.path.expanduser(config) - return os.path.join(self.bin_root(), "bin", "{}{}".format( + return os.path.join(self.bin_root(stage0), "bin", "{}{}".format( program, self.exe_suffix())) @staticmethod @@ -856,14 +880,14 @@ def build_bootstrap(self): if "CARGO_BUILD_TARGET" in env: del env["CARGO_BUILD_TARGET"] env["CARGO_TARGET_DIR"] = build_dir - env["RUSTC"] = self.rustc() - env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ + env["RUSTC"] = self.rustc(True) + env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \ (os.pathsep + env["LD_LIBRARY_PATH"]) \ if "LD_LIBRARY_PATH" in env else "" - env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ + env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \ (os.pathsep + env["DYLD_LIBRARY_PATH"]) \ if "DYLD_LIBRARY_PATH" in env else "" - env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ + env["LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \ (os.pathsep + env["LIBRARY_PATH"]) \ if "LIBRARY_PATH" in env else "" # preserve existing RUSTFLAGS @@ -886,7 +910,7 @@ def build_bootstrap(self): if self.get_toml("deny-warnings", "rust") != "false": env["RUSTFLAGS"] += " -Dwarnings" - env["PATH"] = os.path.join(self.bin_root(), "bin") + \ + env["PATH"] = os.path.join(self.bin_root(True), "bin") + \ os.pathsep + env["PATH"] if not os.path.isfile(self.cargo()): raise Exception("no cargo executable found at `{}`".format( @@ -1137,14 +1161,9 @@ def bootstrap(help_triggered): build.update_submodules() # Fetch/build the bootstrap - build.rustc_commit = build.maybe_download_rustc() - if build.rustc_commit is not None: - if build.verbose: - commit = build.rustc_commit - print("using downloaded stage1 artifacts from CI (commit {})".format(commit)) - # FIXME: support downloading artifacts from the beta channel - build.rustc_channel = "nightly" - build.download_stage0() + build.download_toolchain() + # Download the master compiler if `download-rustc` is set + build.maybe_download_ci_toolchain() sys.stdout.flush() build.ensure_vendored() build.build_bootstrap() @@ -1160,6 +1179,8 @@ def bootstrap(help_triggered): env["RUSTC_BOOTSTRAP"] = '1' if toml_path: env["BOOTSTRAP_CONFIG"] = toml_path + if build.rustc_commit is not None: + env["BOOTSTRAP_DOWNLOAD_RUSTC"] = '1' run(args, env=env, verbose=build.verbose) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 9398f211721b8..8244c7710ab7b 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -65,7 +65,9 @@ impl Step for Std { // These artifacts were already copied (in `impl Step for Sysroot`). // Don't recompile them. - if builder.config.download_rustc { + // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler, + // so its artifacts can't be reused. + if builder.config.download_rustc && compiler.stage != 0 { return; } @@ -513,7 +515,9 @@ impl Step for Rustc { let compiler = self.compiler; let target = self.target; - if builder.config.download_rustc { + // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler, + // so its artifacts can't be reused. + if builder.config.download_rustc && compiler.stage != 0 { // Copy the existing artifacts instead of rebuilding them. // NOTE: this path is only taken for tools linking to rustc-dev. builder.ensure(Sysroot { compiler }); @@ -934,14 +938,15 @@ impl Step for Sysroot { t!(fs::create_dir_all(&sysroot)); // If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0. - if builder.config.download_rustc { + if builder.config.download_rustc && compiler.stage != 0 { assert_eq!( builder.config.build, compiler.host, "Cross-compiling is not yet supported with `download-rustc`", ); // Copy the compiler into the correct sysroot. - let stage0_dir = builder.config.out.join(&*builder.config.build.triple).join("stage0"); - builder.cp_r(&stage0_dir, &sysroot); + let ci_rustc_dir = + builder.config.out.join(&*builder.config.build.triple).join("ci-rustc"); + builder.cp_r(&ci_rustc_dir, &sysroot); return INTERNER.intern_path(sysroot); } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index b9b090bb2d2de..5167cfcb33d80 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -510,7 +510,7 @@ struct Rust { new_symbol_mangling: Option, profile_generate: Option, profile_use: Option, - download_rustc: Option, + download_rustc: Option, } /// TOML representation of how each build target is configured. @@ -687,51 +687,6 @@ impl Config { set(&mut config.print_step_timings, build.print_step_timings); set(&mut config.print_step_rusage, build.print_step_rusage); - // See https://github.com/rust-lang/compiler-team/issues/326 - config.stage = match config.cmd { - Subcommand::Check { .. } => flags.stage.or(build.check_stage).unwrap_or(0), - Subcommand::Doc { .. } => flags.stage.or(build.doc_stage).unwrap_or(0), - Subcommand::Build { .. } => flags.stage.or(build.build_stage).unwrap_or(1), - Subcommand::Test { .. } => flags.stage.or(build.test_stage).unwrap_or(1), - Subcommand::Bench { .. } => flags.stage.or(build.bench_stage).unwrap_or(2), - Subcommand::Dist { .. } => flags.stage.or(build.dist_stage).unwrap_or(2), - Subcommand::Install { .. } => flags.stage.or(build.install_stage).unwrap_or(2), - // These are all bootstrap tools, which don't depend on the compiler. - // The stage we pass shouldn't matter, but use 0 just in case. - Subcommand::Clean { .. } - | Subcommand::Clippy { .. } - | Subcommand::Fix { .. } - | Subcommand::Run { .. } - | Subcommand::Setup { .. } - | Subcommand::Format { .. } => flags.stage.unwrap_or(0), - }; - - // CI should always run stage 2 builds, unless it specifically states otherwise - #[cfg(not(test))] - if flags.stage.is_none() && crate::CiEnv::current() != crate::CiEnv::None { - match config.cmd { - Subcommand::Test { .. } - | Subcommand::Doc { .. } - | Subcommand::Build { .. } - | Subcommand::Bench { .. } - | Subcommand::Dist { .. } - | Subcommand::Install { .. } => { - assert_eq!( - config.stage, 2, - "x.py should be run with `--stage 2` on CI, but was run with `--stage {}`", - config.stage, - ); - } - Subcommand::Clean { .. } - | Subcommand::Check { .. } - | Subcommand::Clippy { .. } - | Subcommand::Fix { .. } - | Subcommand::Run { .. } - | Subcommand::Setup { .. } - | Subcommand::Format { .. } => {} - } - } - config.verbose = cmp::max(config.verbose, flags.verbose); if let Some(install) = toml.install { @@ -897,7 +852,7 @@ impl Config { config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config); config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use); config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate); - config.download_rustc = rust.download_rustc.unwrap_or(false); + config.download_rustc = env::var("BOOTSTRAP_DOWNLOAD_RUSTC").as_deref() == Ok("1"); } else { config.rust_profile_use = flags.rust_profile_use; config.rust_profile_generate = flags.rust_profile_generate; @@ -1005,6 +960,59 @@ impl Config { let default = config.channel == "dev"; config.ignore_git = ignore_git.unwrap_or(default); + let download_rustc = config.download_rustc; + // See https://github.com/rust-lang/compiler-team/issues/326 + config.stage = match config.cmd { + Subcommand::Check { .. } => flags.stage.or(build.check_stage).unwrap_or(0), + // `download-rustc` only has a speed-up for stage2 builds. Default to stage2 unless explicitly overridden. + Subcommand::Doc { .. } => { + flags.stage.or(build.doc_stage).unwrap_or(if download_rustc { 2 } else { 0 }) + } + Subcommand::Build { .. } => { + flags.stage.or(build.build_stage).unwrap_or(if download_rustc { 2 } else { 1 }) + } + Subcommand::Test { .. } => { + flags.stage.or(build.test_stage).unwrap_or(if download_rustc { 2 } else { 1 }) + } + Subcommand::Bench { .. } => flags.stage.or(build.bench_stage).unwrap_or(2), + Subcommand::Dist { .. } => flags.stage.or(build.dist_stage).unwrap_or(2), + Subcommand::Install { .. } => flags.stage.or(build.install_stage).unwrap_or(2), + // These are all bootstrap tools, which don't depend on the compiler. + // The stage we pass shouldn't matter, but use 0 just in case. + Subcommand::Clean { .. } + | Subcommand::Clippy { .. } + | Subcommand::Fix { .. } + | Subcommand::Run { .. } + | Subcommand::Setup { .. } + | Subcommand::Format { .. } => flags.stage.unwrap_or(0), + }; + + // CI should always run stage 2 builds, unless it specifically states otherwise + #[cfg(not(test))] + if flags.stage.is_none() && crate::CiEnv::current() != crate::CiEnv::None { + match config.cmd { + Subcommand::Test { .. } + | Subcommand::Doc { .. } + | Subcommand::Build { .. } + | Subcommand::Bench { .. } + | Subcommand::Dist { .. } + | Subcommand::Install { .. } => { + assert_eq!( + config.stage, 2, + "x.py should be run with `--stage 2` on CI, but was run with `--stage {}`", + config.stage, + ); + } + Subcommand::Clean { .. } + | Subcommand::Check { .. } + | Subcommand::Clippy { .. } + | Subcommand::Fix { .. } + | Subcommand::Run { .. } + | Subcommand::Setup { .. } + | Subcommand::Format { .. } => {} + } + } + config } diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index bfb846f3b562f..f9be35d7c5e0d 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -513,6 +513,19 @@ impl Step for Rustdoc { // rustc compiler it's paired with, so it must be built with the previous stage compiler. let build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build); + // When using `download-rustc` and a stage0 build_compiler, copying rustc doesn't actually + // build stage0 libstd (because the libstd in sysroot has the wrong ABI). Explicitly build + // it. + builder.ensure(compile::Std { compiler: build_compiler, target: target_compiler.host }); + builder.ensure(compile::Rustc { compiler: build_compiler, target: target_compiler.host }); + // NOTE: this implies that `download-rustc` is pretty useless when compiling with the stage0 + // compiler, since you do just as much work. + if !builder.config.dry_run && builder.config.download_rustc && build_compiler.stage == 0 { + println!( + "warning: `download-rustc` does nothing when building stage1 tools; consider using `--stage 2` instead" + ); + } + // The presence of `target_compiler` ensures that the necessary libraries (codegen backends, // compiler libraries, ...) are built. Rustdoc does not require the presence of any // libraries within sysroot_libdir (i.e., rustlib), though doctests may want it (since diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index 25d8629a38f1b..946c354fd9d9c 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -501,7 +501,7 @@ Here is the list of currently supported register classes: | x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `r[8-15]` (x86-64 only) | `r` | | x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` | | x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` | -| x86-64 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b`, `ah`\*, `bh`\*, `ch`\*, `dh`\* | `q` | +| x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b` | `q` | | x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` | | x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` | | x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` | @@ -532,7 +532,7 @@ Here is the list of currently supported register classes: > **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register. > -> Note #2: On x86-64 the high byte registers (e.g. `ah`) are only available when used as an explicit register. Specifying the `reg_byte` register class for an operand will always allocate a low byte register. +> Note #2: On x86-64 the high byte registers (e.g. `ah`) are not available in the `reg_byte` register class. > > Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported. > diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 60cbe9f376f01..9c0ed1480fef3 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -251,19 +251,9 @@ crate fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { debug!("trying to get a name from pattern: {:?}", p); Symbol::intern(&match p.kind { - PatKind::Wild => return kw::Underscore, + PatKind::Wild | PatKind::Struct(..) => return kw::Underscore, PatKind::Binding(_, _, ident, _) => return ident.name, PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), - PatKind::Struct(ref name, ref fields, etc) => format!( - "{} {{ {}{} }}", - qpath_to_string(name), - fields - .iter() - .map(|fp| format!("{}: {}", fp.ident, name_from_pat(&fp.pat))) - .collect::>() - .join(", "), - if etc { ", .." } else { "" } - ), PatKind::Or(ref pats) => pats .iter() .map(|p| name_from_pat(&**p).to_string()) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 7b656baa1b4bf..bf57e3c37d3fb 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1915,13 +1915,6 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { } if v.iter().any(|i| i.inner_impl().trait_.is_some()) { - if let Some(impl_) = v - .iter() - .filter(|i| i.inner_impl().trait_.is_some()) - .find(|i| i.inner_impl().trait_.def_id_full(cache) == cx.cache.deref_trait_did) - { - sidebar_deref_methods(cx, out, impl_, v); - } let format_impls = |impls: Vec<&Impl>| { let mut links = FxHashSet::default(); @@ -1989,6 +1982,14 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { ); write_sidebar_links(out, blanket_format); } + + if let Some(impl_) = v + .iter() + .filter(|i| i.inner_impl().trait_.is_some()) + .find(|i| i.inner_impl().trait_.def_id_full(cache) == cx.cache.deref_trait_did) + { + sidebar_deref_methods(cx, out, impl_, v); + } } } } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 437f42b26dd11..39912a136ab22 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -368,55 +368,28 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } /// Given a primitive type, try to resolve an associated item. - /// - /// HACK(jynelson): `item_str` is passed in instead of derived from `item_name` so the - /// lifetimes on `&'path` will work. fn resolve_primitive_associated_item( &self, prim_ty: PrimitiveType, ns: Namespace, - module_id: DefId, item_name: Symbol, - item_str: &'path str, - ) -> Result<(Res, Option), ErrorKind<'path>> { + ) -> Option<(Res, String, Option<(DefKind, DefId)>)> { let tcx = self.cx.tcx; - prim_ty - .impls(tcx) - .into_iter() - .find_map(|&impl_| { - tcx.associated_items(impl_) - .find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, impl_) - .map(|item| { - let kind = item.kind; - self.kind_side_channel.set(Some((kind.as_def_kind(), item.def_id))); - match kind { - ty::AssocKind::Fn => "method", - ty::AssocKind::Const => "associatedconstant", - ty::AssocKind::Type => "associatedtype", - } - }) - .map(|out| { - ( - Res::Primitive(prim_ty), - Some(format!("{}#{}.{}", prim_ty.as_str(), out, item_str)), - ) - }) - }) - .ok_or_else(|| { - debug!( - "returning primitive error for {}::{} in {} namespace", - prim_ty.as_str(), - item_name, - ns.descr() - ); - ResolutionFailure::NotResolved { - module_id, - partial_res: Some(Res::Primitive(prim_ty)), - unresolved: item_str.into(), - } - .into() - }) + prim_ty.impls(tcx).into_iter().find_map(|&impl_| { + tcx.associated_items(impl_) + .find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, impl_) + .map(|item| { + let kind = item.kind; + let out = match kind { + ty::AssocKind::Fn => "method", + ty::AssocKind::Const => "associatedconstant", + ty::AssocKind::Type => "associatedtype", + }; + let fragment = format!("{}#{}.{}", prim_ty.as_str(), out, item_name); + (Res::Primitive(prim_ty), fragment, Some((kind.as_def_kind(), item.def_id))) + }) + }) } /// Resolves a string as a macro. @@ -490,8 +463,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { module_id: DefId, extra_fragment: &Option, ) -> Result<(Res, Option), ErrorKind<'path>> { - let tcx = self.cx.tcx; - if let Some(res) = self.resolve_path(path_str, ns, module_id) { match res { // FIXME(#76467): make this fallthrough to lookup the associated @@ -534,29 +505,58 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } })?; - // FIXME: are these both necessary? - let ty_res = if let Some(ty_res) = resolve_primitive(&path_root, TypeNS) + // FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support + // links to primitives when `#[doc(primitive)]` is present. It should give an ambiguity + // error instead and special case *only* modules with `#[doc(primitive)]`, not all + // primitives. + resolve_primitive(&path_root, TypeNS) .or_else(|| self.resolve_path(&path_root, TypeNS, module_id)) - { - ty_res - } else { - // FIXME: this is duplicated on the end of this function. - return if ns == Namespace::ValueNS { - self.variant_field(path_str, module_id) - } else { - Err(ResolutionFailure::NotResolved { - module_id, - partial_res: None, - unresolved: path_root.into(), + .and_then(|ty_res| { + let (res, fragment, side_channel) = + self.resolve_associated_item(ty_res, item_name, ns, module_id)?; + let result = if extra_fragment.is_some() { + let diag_res = side_channel.map_or(res, |(k, r)| Res::Def(k, r)); + Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(diag_res))) + } else { + // HACK(jynelson): `clean` expects the type, not the associated item + // but the disambiguator logic expects the associated item. + // Store the kind in a side channel so that only the disambiguator logic looks at it. + if let Some((kind, id)) = side_channel { + self.kind_side_channel.set(Some((kind, id))); + } + Ok((res, Some(fragment))) + }; + Some(result) + }) + .unwrap_or_else(|| { + if ns == Namespace::ValueNS { + self.variant_field(path_str, module_id) + } else { + Err(ResolutionFailure::NotResolved { + module_id, + partial_res: None, + unresolved: path_root.into(), + } + .into()) } - .into()) - }; - }; + }) + } - let res = match ty_res { - Res::Primitive(prim) => Some( - self.resolve_primitive_associated_item(prim, ns, module_id, item_name, item_str), - ), + /// Returns: + /// - None if no associated item was found + /// - Some((_, _, Some(_))) if an item was found and should go through a side channel + /// - Some((_, _, None)) otherwise + fn resolve_associated_item( + &mut self, + root_res: Res, + item_name: Symbol, + ns: Namespace, + module_id: DefId, + ) -> Option<(Res, String, Option<(DefKind, DefId)>)> { + let tcx = self.cx.tcx; + + match root_res { + Res::Primitive(prim) => self.resolve_primitive_associated_item(prim, ns, item_name), Res::Def( DefKind::Struct | DefKind::Union @@ -599,59 +599,42 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ty::AssocKind::Const => "associatedconstant", ty::AssocKind::Type => "associatedtype", }; - Some(if extra_fragment.is_some() { - Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res))) - } else { - // HACK(jynelson): `clean` expects the type, not the associated item - // but the disambiguator logic expects the associated item. - // Store the kind in a side channel so that only the disambiguator logic looks at it. - self.kind_side_channel.set(Some((kind.as_def_kind(), id))); - Ok((ty_res, Some(format!("{}.{}", out, item_str)))) - }) - } else if ns == Namespace::ValueNS { - debug!("looking for variants or fields named {} for {:?}", item_name, did); - // FIXME(jynelson): why is this different from - // `variant_field`? - match tcx.type_of(did).kind() { - ty::Adt(def, _) => { - let field = if def.is_enum() { - def.all_fields().find(|item| item.ident.name == item_name) - } else { - def.non_enum_variant() - .fields - .iter() - .find(|item| item.ident.name == item_name) - }; - field.map(|item| { - if extra_fragment.is_some() { - let res = Res::Def( - if def.is_enum() { - DefKind::Variant - } else { - DefKind::Field - }, - item.did, - ); - Err(ErrorKind::AnchorFailure( - AnchorFailure::RustdocAnchorConflict(res), - )) - } else { - Ok(( - ty_res, - Some(format!( - "{}.{}", - if def.is_enum() { "variant" } else { "structfield" }, - item.ident - )), - )) - } - }) - } - _ => None, - } - } else { - None + // HACK(jynelson): `clean` expects the type, not the associated item + // but the disambiguator logic expects the associated item. + // Store the kind in a side channel so that only the disambiguator logic looks at it. + return Some(( + root_res, + format!("{}.{}", out, item_name), + Some((kind.as_def_kind(), id)), + )); + } + + if ns != Namespace::ValueNS { + return None; } + debug!("looking for variants or fields named {} for {:?}", item_name, did); + // FIXME: this doesn't really belong in `associated_item` (maybe `variant_field` is better?) + // NOTE: it's different from variant_field because it resolves fields and variants, + // not variant fields (2 path segments, not 3). + let def = match tcx.type_of(did).kind() { + ty::Adt(def, _) => def, + _ => return None, + }; + let field = if def.is_enum() { + def.all_fields().find(|item| item.ident.name == item_name) + } else { + def.non_enum_variant().fields.iter().find(|item| item.ident.name == item_name) + }?; + let kind = if def.is_enum() { DefKind::Variant } else { DefKind::Field }; + Some(( + root_res, + format!( + "{}.{}", + if def.is_enum() { "variant" } else { "structfield" }, + field.ident + ), + Some((kind, field.did)), + )) } Res::Def(DefKind::Trait, did) => tcx .associated_items(did) @@ -669,27 +652,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } }; - if extra_fragment.is_some() { - Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res))) - } else { - let res = Res::Def(item.kind.as_def_kind(), item.def_id); - Ok((res, Some(format!("{}.{}", kind, item_str)))) - } + let res = Res::Def(item.kind.as_def_kind(), item.def_id); + (res, format!("{}.{}", kind, item_name), None) }), _ => None, - }; - res.unwrap_or_else(|| { - if ns == Namespace::ValueNS { - self.variant_field(path_str, module_id) - } else { - Err(ResolutionFailure::NotResolved { - module_id, - partial_res: Some(ty_res), - unresolved: item_str.into(), - } - .into()) - } - }) + } } /// Used for reporting better errors. @@ -978,14 +945,18 @@ impl LinkCollector<'_, '_> { Ok(Some((d, path))) => (path.trim(), Some(d)), Ok(None) => (link.trim(), None), Err((err_msg, relative_range)) => { - let disambiguator_range = (no_backticks_range.start + relative_range.start) - ..(no_backticks_range.start + relative_range.end); - disambiguator_error(self.cx, &item, dox, disambiguator_range, &err_msg); + if !should_ignore_link_with_disambiguators(link) { + // Only report error if we would not have ignored this link. + // See issue #83859. + let disambiguator_range = (no_backticks_range.start + relative_range.start) + ..(no_backticks_range.start + relative_range.end); + disambiguator_error(self.cx, &item, dox, disambiguator_range, &err_msg); + } return None; } }; - if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ":_<>, !*&;".contains(ch))) { + if should_ignore_link(path_str) { return None; } @@ -1515,6 +1486,22 @@ fn range_between_backticks(ori_link: &MarkdownLink) -> Range { ..(ori_link.range.start + before_second_backtick_group) } +/// Returns true if we should ignore `link` due to it being unlikely +/// that it is an intra-doc link. `link` should still have disambiguators +/// if there were any. +/// +/// The difference between this and [`should_ignore_link()`] is that this +/// check should only be used on links that still have disambiguators. +fn should_ignore_link_with_disambiguators(link: &str) -> bool { + link.contains(|ch: char| !(ch.is_alphanumeric() || ":_<>, !*&;@()".contains(ch))) +} + +/// Returns true if we should ignore `path_str` due to it being unlikely +/// that it is an intra-doc link. +fn should_ignore_link(path_str: &str) -> bool { + path_str.contains(|ch: char| !(ch.is_alphanumeric() || ":_<>, !*&;".contains(ch))) +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] /// Disambiguators for a link. crate enum Disambiguator { diff --git a/src/test/rustdoc-ui/intra-doc/email-address-localhost.rs b/src/test/rustdoc-ui/intra-doc/email-address-localhost.rs new file mode 100644 index 0000000000000..417618c74582c --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/email-address-localhost.rs @@ -0,0 +1,6 @@ +#![deny(warnings)] + +//! Email me at . +//~^ ERROR unknown disambiguator `hello` + +//! This should *not* warn: . diff --git a/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr b/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr new file mode 100644 index 0000000000000..de215b2163bd4 --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr @@ -0,0 +1,15 @@ +error: unknown disambiguator `hello` + --> $DIR/email-address-localhost.rs:3:18 + | +LL | //! Email me at . + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/email-address-localhost.rs:1:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(warnings)]` + +error: aborting due to previous error + diff --git a/src/test/rustdoc/intra-doc/email-address.rs b/src/test/rustdoc/intra-doc/email-address.rs new file mode 100644 index 0000000000000..c407eb80da225 --- /dev/null +++ b/src/test/rustdoc/intra-doc/email-address.rs @@ -0,0 +1,6 @@ +//! Email me at . +//! Email me at . +//! Email me at (this warns but will still become a link). +// @has email_address/index.html '//a[@href="mailto:hello@example.com"]' 'hello@example.com' +// @has email_address/index.html '//a[@href="mailto:hello-world@example.com"]' 'hello-world@example.com' +// @has email_address/index.html '//a[@href="mailto:hello@localhost"]' 'hello@localhost' diff --git a/src/test/rustdoc/struct-arg-pattern.rs b/src/test/rustdoc/struct-arg-pattern.rs new file mode 100644 index 0000000000000..3c0369e3d3413 --- /dev/null +++ b/src/test/rustdoc/struct-arg-pattern.rs @@ -0,0 +1,10 @@ +#![crate_name = "foo"] + +struct BodyId { + hir_id: usize, +} + +// @has 'foo/fn.body_owner.html' '//*[@class="rust fn"]' 'pub fn body_owner(_: BodyId)' +pub fn body_owner(BodyId { hir_id }: BodyId) { + // ... +} diff --git a/src/test/ui/asm/bad-reg.rs b/src/test/ui/asm/bad-reg.rs index 016ea9329c4d0..da302b248760f 100644 --- a/src/test/ui/asm/bad-reg.rs +++ b/src/test/ui/asm/bad-reg.rs @@ -37,6 +37,8 @@ fn main() { //~^ ERROR invalid register `mm0`: MMX registers are not currently supported as operands asm!("", in("k0") foo); //~^ ERROR invalid register `k0`: the k0 AVX mask register cannot be used as an operand + asm!("", in("ah") foo); + //~^ ERROR invalid register `ah`: high byte registers cannot be used as an operand // Explicit register conflicts // (except in/lateout which don't conflict) diff --git a/src/test/ui/asm/bad-reg.stderr b/src/test/ui/asm/bad-reg.stderr index c6b7d310dfa6c..2bfb4854c3442 100644 --- a/src/test/ui/asm/bad-reg.stderr +++ b/src/test/ui/asm/bad-reg.stderr @@ -94,8 +94,14 @@ error: invalid register `k0`: the k0 AVX mask register cannot be used as an oper LL | asm!("", in("k0") foo); | ^^^^^^^^^^^^ +error: invalid register `ah`: high byte registers cannot be used as an operand on x86_64 + --> $DIR/bad-reg.rs:40:18 + | +LL | asm!("", in("ah") foo); + | ^^^^^^^^^^^^ + error: register `al` conflicts with register `ax` - --> $DIR/bad-reg.rs:44:33 + --> $DIR/bad-reg.rs:46:33 | LL | asm!("", in("eax") foo, in("al") bar); | ------------- ^^^^^^^^^^^^ register `al` @@ -103,7 +109,7 @@ LL | asm!("", in("eax") foo, in("al") bar); | register `ax` error: register `ax` conflicts with register `ax` - --> $DIR/bad-reg.rs:46:33 + --> $DIR/bad-reg.rs:48:33 | LL | asm!("", in("rax") foo, out("rax") bar); | ------------- ^^^^^^^^^^^^^^ register `ax` @@ -111,13 +117,13 @@ LL | asm!("", in("rax") foo, out("rax") bar); | register `ax` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:46:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", in("rax") foo, out("rax") bar); | ^^^^^^^^^^^^^ error: register `ymm0` conflicts with register `xmm0` - --> $DIR/bad-reg.rs:49:34 + --> $DIR/bad-reg.rs:51:34 | LL | asm!("", in("xmm0") foo, in("ymm0") bar); | -------------- ^^^^^^^^^^^^^^ register `ymm0` @@ -125,7 +131,7 @@ LL | asm!("", in("xmm0") foo, in("ymm0") bar); | register `xmm0` error: register `ymm0` conflicts with register `xmm0` - --> $DIR/bad-reg.rs:51:34 + --> $DIR/bad-reg.rs:53:34 | LL | asm!("", in("xmm0") foo, out("ymm0") bar); | -------------- ^^^^^^^^^^^^^^^ register `ymm0` @@ -133,10 +139,10 @@ LL | asm!("", in("xmm0") foo, out("ymm0") bar); | register `xmm0` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:51:18 + --> $DIR/bad-reg.rs:53:18 | LL | asm!("", in("xmm0") foo, out("ymm0") bar); | ^^^^^^^^^^^^^^ -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors