diff options
66 files changed, 827 insertions, 412 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25397006ee2..51dd0f81ed1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,8 @@ jobs: env: CI_JOB_NAME: ${{ matrix.name }} CI_JOB_DOC_URL: ${{ matrix.doc_url }} + GITHUB_WORKFLOW_RUN_ID: ${{ github.run_id }} + GITHUB_REPOSITORY: ${{ github.repository }} CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse # commit of PR sha or commit sha. `GITHUB_SHA` is not accurate for PRs. HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a1487ca74be..da739b0e453 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -334,8 +334,7 @@ impl<'a> AstValidator<'a> { .filter(|attr| { let arr = [ sym::allow, - sym::cfg, - sym::cfg_attr, + sym::cfg_trace, sym::cfg_attr_trace, sym::deny, sym::expect, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index cdb18179449..3dbfc191f8f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -593,7 +593,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere } fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) -> bool { - if attr.has_name(sym::cfg_attr_trace) { + if attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) { // It's not a valid identifier, so avoid printing it // to keep the printed code reasonably parse-able. return false; diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 95912b01600..954a6014809 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -10,8 +10,6 @@ codegen_ssa_apple_deployment_target_invalid = codegen_ssa_apple_deployment_target_too_low = deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min} -codegen_ssa_apple_sdk_error_sdk_path = failed to get {$sdk_name} SDK path: {$error} - codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error} codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering @@ -391,8 +389,6 @@ codegen_ssa_unknown_atomic_ordering = unknown ordering in atomic intrinsic codegen_ssa_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified -codegen_ssa_unsupported_arch = unsupported arch `{$arch}` for os `{$os}` - codegen_ssa_unsupported_instruction_set = target does not support `#[instruction_set]` codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target @@ -402,3 +398,20 @@ codegen_ssa_use_cargo_directive = use the `cargo:rustc-link-lib` directive to sp codegen_ssa_version_script_write_failure = failed to write version script: {$error} codegen_ssa_visual_studio_not_installed = you may need to install Visual Studio build tools with the "C++ build tools" workload + +codegen_ssa_xcrun_command_line_tools_insufficient = + when compiling for iOS, tvOS, visionOS or watchOS, you need a full installation of Xcode + +codegen_ssa_xcrun_failed_invoking = invoking `{$command_formatted}` to find {$sdk_name}.sdk failed: {$error} + +codegen_ssa_xcrun_found_developer_dir = found active developer directory at "{$developer_dir}" + +# `xcrun` already outputs a message about missing Xcode installation, so we only augment it with details about env vars. +codegen_ssa_xcrun_no_developer_dir = + pass the path of an Xcode installation via the DEVELOPER_DIR environment variable, or an SDK with the SDKROOT environment variable + +codegen_ssa_xcrun_sdk_path_warning = output of `xcrun` while finding {$sdk_name}.sdk + .note = {$stderr} + +codegen_ssa_xcrun_unsuccessful = failed running `{$command_formatted}` to find {$sdk_name}.sdk + .note = {$stdout}{$stderr} diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs index bfa7635a869..2c8b0ec418d 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple.rs @@ -1,16 +1,40 @@ use std::env; +use std::ffi::OsString; use std::fmt::{Display, from_fn}; use std::num::ParseIntError; +use std::path::PathBuf; +use std::process::Command; +use itertools::Itertools; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::Session; use rustc_target::spec::Target; +use tracing::debug; -use crate::errors::AppleDeploymentTarget; +use crate::errors::{AppleDeploymentTarget, XcrunError, XcrunSdkPathWarning}; +use crate::fluent_generated as fluent; #[cfg(test)] mod tests; +/// The canonical name of the desired SDK for a given target. +pub(super) fn sdk_name(target: &Target) -> &'static str { + match (&*target.os, &*target.abi) { + ("macos", "") => "MacOSX", + ("ios", "") => "iPhoneOS", + ("ios", "sim") => "iPhoneSimulator", + // Mac Catalyst uses the macOS SDK + ("ios", "macabi") => "MacOSX", + ("tvos", "") => "AppleTVOS", + ("tvos", "sim") => "AppleTVSimulator", + ("visionos", "") => "XROS", + ("visionos", "sim") => "XRSimulator", + ("watchos", "") => "WatchOS", + ("watchos", "sim") => "WatchSimulator", + (os, abi) => unreachable!("invalid os '{os}' / abi '{abi}' combination for Apple target"), + } +} + pub(super) fn macho_platform(target: &Target) -> u32 { match (&*target.os, &*target.abi) { ("macos", _) => object::macho::PLATFORM_MACOS, @@ -253,3 +277,131 @@ pub(super) fn add_version_to_llvm_target( format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}") } } + +pub(super) fn get_sdk_root(sess: &Session) -> Option<PathBuf> { + let sdk_name = sdk_name(&sess.target); + + match xcrun_show_sdk_path(sdk_name, sess.verbose_internals()) { + Ok((path, stderr)) => { + // Emit extra stderr, such as if `-verbose` was passed, or if `xcrun` emitted a warning. + if !stderr.is_empty() { + sess.dcx().emit_warn(XcrunSdkPathWarning { sdk_name, stderr }); + } + Some(path) + } + Err(err) => { + let mut diag = sess.dcx().create_err(err); + + // Recognize common error cases, and give more Rust-specific error messages for those. + if let Some(developer_dir) = xcode_select_developer_dir() { + diag.arg("developer_dir", &developer_dir); + diag.note(fluent::codegen_ssa_xcrun_found_developer_dir); + if developer_dir.as_os_str().to_string_lossy().contains("CommandLineTools") { + if sdk_name != "MacOSX" { + diag.help(fluent::codegen_ssa_xcrun_command_line_tools_insufficient); + } + } + } else { + diag.help(fluent::codegen_ssa_xcrun_no_developer_dir); + } + + diag.emit(); + None + } + } +} + +/// Invoke `xcrun --sdk $sdk_name --show-sdk-path` to get the SDK path. +/// +/// The exact logic that `xcrun` uses is unspecified (see `man xcrun` for a few details), and may +/// change between macOS and Xcode versions, but it roughly boils down to finding the active +/// developer directory, and then invoking `xcodebuild -sdk $sdk_name -version` to get the SDK +/// details. +/// +/// Finding the developer directory is roughly done by looking at, in order: +/// - The `DEVELOPER_DIR` environment variable. +/// - The `/var/db/xcode_select_link` symlink (set by `xcode-select --switch`). +/// - `/Applications/Xcode.app` (hardcoded fallback path). +/// - `/Library/Developer/CommandLineTools` (hardcoded fallback path). +/// +/// Note that `xcrun` caches its result, but with a cold cache this whole operation can be quite +/// slow, especially so the first time it's run after a reboot. +fn xcrun_show_sdk_path( + sdk_name: &'static str, + verbose: bool, +) -> Result<(PathBuf, String), XcrunError> { + let mut cmd = Command::new("xcrun"); + if verbose { + cmd.arg("--verbose"); + } + // The `--sdk` parameter is the same as in xcodebuild, namely either an absolute path to an SDK, + // or the (lowercase) canonical name of an SDK. + cmd.arg("--sdk"); + cmd.arg(&sdk_name.to_lowercase()); + cmd.arg("--show-sdk-path"); + + // We do not stream stdout/stderr lines directly to the user, since whether they are warnings or + // errors depends on the status code at the end. + let output = cmd.output().map_err(|error| XcrunError::FailedInvoking { + sdk_name, + command_formatted: format!("{cmd:?}"), + error, + })?; + + // It is fine to do lossy conversion here, non-UTF-8 paths are quite rare on macOS nowadays + // (only possible with the HFS+ file system), and we only use it for error messages. + let stderr = String::from_utf8_lossy_owned(output.stderr); + if !stderr.is_empty() { + debug!(stderr, "original xcrun stderr"); + } + + // Some versions of `xcodebuild` output beefy errors when invoked via `xcrun`, + // but these are usually red herrings. + let stderr = stderr + .lines() + .filter(|line| { + !line.contains("Writing error result bundle") + && !line.contains("Requested but did not find extension point with identifier") + }) + .join("\n"); + + if output.status.success() { + Ok((stdout_to_path(output.stdout), stderr)) + } else { + // Output both stdout and stderr, since shims of `xcrun` (such as the one provided by + // nixpkgs), do not always use stderr for errors. + let stdout = String::from_utf8_lossy_owned(output.stdout).trim().to_string(); + Err(XcrunError::Unsuccessful { + sdk_name, + command_formatted: format!("{cmd:?}"), + stdout, + stderr, + }) + } +} + +/// Invoke `xcode-select --print-path`, and return the current developer directory. +/// +/// NOTE: We don't do any error handling here, this is only used as a canary in diagnostics (`xcrun` +/// will have already emitted the relevant error information). +fn xcode_select_developer_dir() -> Option<PathBuf> { + let mut cmd = Command::new("xcode-select"); + cmd.arg("--print-path"); + let output = cmd.output().ok()?; + if !output.status.success() { + return None; + } + Some(stdout_to_path(output.stdout)) +} + +fn stdout_to_path(mut stdout: Vec<u8>) -> PathBuf { + // Remove trailing newline. + if let Some(b'\n') = stdout.last() { + let _ = stdout.pop().unwrap(); + } + #[cfg(unix)] + let path = <OsString as std::os::unix::ffi::OsStringExt>::from_vec(stdout); + #[cfg(not(unix))] // Unimportant, this is only used on macOS + let path = OsString::from(String::from_utf8(stdout).unwrap()); + PathBuf::from(path) +} diff --git a/compiler/rustc_codegen_ssa/src/back/apple/tests.rs b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs index 7ccda5a8190..8df740a4bcf 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs @@ -1,4 +1,4 @@ -use super::{add_version_to_llvm_target, parse_version}; +use super::*; #[test] fn test_add_version_to_llvm_target() { @@ -19,3 +19,69 @@ fn test_parse_version() { assert_eq!(parse_version("10.12.6"), Ok((10, 12, 6))); assert_eq!(parse_version("9999.99.99"), Ok((9999, 99, 99))); } + +#[test] +#[cfg_attr(not(target_os = "macos"), ignore = "xcode-select is only available on macOS")] +fn lookup_developer_dir() { + let _developer_dir = xcode_select_developer_dir().unwrap(); +} + +#[test] +#[cfg_attr(not(target_os = "macos"), ignore = "xcrun is only available on macOS")] +fn lookup_sdk() { + let (sdk_path, stderr) = xcrun_show_sdk_path("MacOSX", false).unwrap(); + // Check that the found SDK is valid. + assert!(sdk_path.join("SDKSettings.plist").exists()); + assert_eq!(stderr, ""); + + // Test that the SDK root is a subdir of the developer directory. + if let Some(developer_dir) = xcode_select_developer_dir() { + // Only run this test if SDKROOT is not set (otherwise xcrun may look up via. that). + if std::env::var_os("SDKROOT").is_some() { + assert!(sdk_path.starts_with(&developer_dir)); + } + } +} + +#[test] +#[cfg_attr(not(target_os = "macos"), ignore = "xcrun is only available on macOS")] +fn lookup_sdk_verbose() { + let (_, stderr) = xcrun_show_sdk_path("MacOSX", true).unwrap(); + // Newer xcrun versions should emit something like this: + // + // xcrun: note: looking up SDK with 'xcodebuild -sdk macosx -version Path' + // xcrun: note: xcrun_db = '/var/.../xcrun_db' + // xcrun: note: lookup resolved to: '...' + // xcrun: note: database key is: ... + // + // Or if the value is already cached, something like this: + // + // xcrun: note: database key is: ... + // xcrun: note: lookup resolved in '/var/.../xcrun_db' : '...' + assert!( + stderr.contains("xcrun: note: lookup resolved"), + "stderr should contain lookup note: {stderr}", + ); +} + +#[test] +#[cfg_attr(not(target_os = "macos"), ignore = "xcrun is only available on macOS")] +fn try_lookup_invalid_sdk() { + // As a proxy for testing all the different ways that `xcrun` can fail, + // test the case where an SDK was not found. + let err = xcrun_show_sdk_path("invalid", false).unwrap_err(); + let XcrunError::Unsuccessful { stderr, .. } = err else { + panic!("unexpected error kind: {err:?}"); + }; + // Either one of (depending on if using Command Line Tools or full Xcode): + // xcrun: error: SDK "invalid" cannot be located + // xcodebuild: error: SDK "invalid" cannot be located. + assert!( + stderr.contains(r#"error: SDK "invalid" cannot be located"#), + "stderr should contain xcodebuild note: {stderr}", + ); + assert!( + stderr.contains("xcrun: error: unable to lookup item 'Path' in SDK 'invalid'"), + "stderr should contain xcrun note: {stderr}", + ); +} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 2e614a1f06f..b59d73a9aae 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3201,9 +3201,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo } fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> Option<PathBuf> { - let arch = &sess.target.arch; let os = &sess.target.os; - let llvm_target = &sess.target.llvm_target; if sess.target.vendor != "apple" || !matches!(os.as_ref(), "ios" | "tvos" | "watchos" | "visionos" | "macos") || !matches!(flavor, LinkerFlavor::Darwin(..)) @@ -3215,37 +3213,7 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> return None; } - let sdk_name = match (arch.as_ref(), os.as_ref()) { - ("aarch64", "tvos") if llvm_target.ends_with("-simulator") => "appletvsimulator", - ("aarch64", "tvos") => "appletvos", - ("x86_64", "tvos") => "appletvsimulator", - ("arm", "ios") => "iphoneos", - ("aarch64", "ios") if llvm_target.contains("macabi") => "macosx", - ("aarch64", "ios") if llvm_target.ends_with("-simulator") => "iphonesimulator", - ("aarch64", "ios") => "iphoneos", - ("x86", "ios") => "iphonesimulator", - ("x86_64", "ios") if llvm_target.contains("macabi") => "macosx", - ("x86_64", "ios") => "iphonesimulator", - ("x86_64", "watchos") => "watchsimulator", - ("arm64_32", "watchos") => "watchos", - ("aarch64", "watchos") if llvm_target.ends_with("-simulator") => "watchsimulator", - ("aarch64", "watchos") => "watchos", - ("aarch64", "visionos") if llvm_target.ends_with("-simulator") => "xrsimulator", - ("aarch64", "visionos") => "xros", - ("arm", "watchos") => "watchos", - (_, "macos") => "macosx", - _ => { - sess.dcx().emit_err(errors::UnsupportedArch { arch, os }); - return None; - } - }; - let sdk_root = match get_apple_sdk_root(sdk_name) { - Ok(s) => s, - Err(e) => { - sess.dcx().emit_err(e); - return None; - } - }; + let sdk_root = sess.time("get_apple_sdk_root", || get_apple_sdk_root(sess))?; match flavor { LinkerFlavor::Darwin(Cc::Yes, _) => { @@ -3255,28 +3223,32 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> // This is admittedly a bit strange, as on most targets // `-isysroot` only applies to include header files, but on Apple // targets this also applies to libraries and frameworks. - cmd.cc_args(&["-isysroot", &sdk_root]); + cmd.cc_arg("-isysroot"); + cmd.cc_arg(&sdk_root); } LinkerFlavor::Darwin(Cc::No, _) => { - cmd.link_args(&["-syslibroot", &sdk_root]); + cmd.link_arg("-syslibroot"); + cmd.link_arg(&sdk_root); } _ => unreachable!(), } - Some(sdk_root.into()) + Some(sdk_root) } -fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootError<'_>> { - // Following what clang does - // (https://github.com/llvm/llvm-project/blob/ - // 296a80102a9b72c3eda80558fb78a3ed8849b341/clang/lib/Driver/ToolChains/Darwin.cpp#L1661-L1678) - // to allow the SDK path to be set. (For clang, xcrun sets - // SDKROOT; for rustc, the user or build system can set it, or we - // can fall back to checking for xcrun on PATH.) +fn get_apple_sdk_root(sess: &Session) -> Option<PathBuf> { if let Ok(sdkroot) = env::var("SDKROOT") { - let p = Path::new(&sdkroot); - match sdk_name { - // Ignore `SDKROOT` if it's clearly set for the wrong platform. + let p = PathBuf::from(&sdkroot); + + // Ignore invalid SDKs, similar to what clang does: + // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.6/clang/lib/Driver/ToolChains/Darwin.cpp#L2212-L2229 + // + // NOTE: Things are complicated here by the fact that `rustc` can be run by Cargo to compile + // build scripts and proc-macros for the host, and thus we need to ignore SDKROOT if it's + // clearly set for the wrong platform. + // + // FIXME(madsmtm): Make this more robust (maybe read `SDKSettings.json` like Clang does?). + match &*apple::sdk_name(&sess.target).to_lowercase() { "appletvos" if sdkroot.contains("TVSimulator.platform") || sdkroot.contains("MacOSX.platform") => {} @@ -3303,26 +3275,11 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootErro if sdkroot.contains("XROS.platform") || sdkroot.contains("MacOSX.platform") => {} // Ignore `SDKROOT` if it's not a valid path. _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} - _ => return Ok(sdkroot), + _ => return Some(p), } } - let res = - Command::new("xcrun").arg("--show-sdk-path").arg("-sdk").arg(sdk_name).output().and_then( - |output| { - if output.status.success() { - Ok(String::from_utf8(output.stdout).unwrap()) - } else { - let error = String::from_utf8(output.stderr); - let error = format!("process exit with error: {}", error.unwrap()); - Err(io::Error::new(io::ErrorKind::Other, &error[..])) - } - }, - ); - match res { - Ok(output) => Ok(output.trim().to_string()), - Err(error) => Err(errors::AppleSdkRootError::SdkPath { sdk_name, error }), - } + apple::get_sdk_root(sess) } /// When using the linker flavors opting in to `lld`, add the necessary paths and arguments to diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 0b7cad0c2fd..f52d29baf9d 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -739,13 +739,6 @@ pub enum ExtractBundledLibsError<'a> { } #[derive(Diagnostic)] -#[diag(codegen_ssa_unsupported_arch)] -pub(crate) struct UnsupportedArch<'a> { - pub arch: &'a str, - pub os: &'a str, -} - -#[derive(Diagnostic)] pub(crate) enum AppleDeploymentTarget { #[diag(codegen_ssa_apple_deployment_target_invalid)] Invalid { env_var: &'static str, error: ParseIntError }, @@ -754,12 +747,6 @@ pub(crate) enum AppleDeploymentTarget { } #[derive(Diagnostic)] -pub(crate) enum AppleSdkRootError<'a> { - #[diag(codegen_ssa_apple_sdk_error_sdk_path)] - SdkPath { sdk_name: &'a str, error: Error }, -} - -#[derive(Diagnostic)] #[diag(codegen_ssa_read_file)] pub(crate) struct ReadFileError { pub message: std::io::Error, @@ -1334,3 +1321,26 @@ pub(crate) struct MixedExportNameAndNoMangle { #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] pub removal_span: Span, } + +#[derive(Diagnostic, Debug)] +pub(crate) enum XcrunError { + #[diag(codegen_ssa_xcrun_failed_invoking)] + FailedInvoking { sdk_name: &'static str, command_formatted: String, error: std::io::Error }, + + #[diag(codegen_ssa_xcrun_unsuccessful)] + #[note] + Unsuccessful { + sdk_name: &'static str, + command_formatted: String, + stdout: String, + stderr: String, + }, +} + +#[derive(Diagnostic, Debug)] +#[diag(codegen_ssa_xcrun_sdk_path_warning)] +#[note] +pub(crate) struct XcrunSdkPathWarning { + pub sdk_name: &'static str, + pub stderr: String, +} diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 93c34a2f576..d26d6edf314 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -13,6 +13,7 @@ #![feature(let_chains)] #![feature(negative_impls)] #![feature(rustdoc_internals)] +#![feature(string_from_utf8_lossy_owned)] #![feature(trait_alias)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index ada49eef7b2..bcc2703c39b 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -156,6 +156,19 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec .collect() } +pub(crate) fn attr_into_trace(mut attr: Attribute, trace_name: Symbol) -> Attribute { + match &mut attr.kind { + AttrKind::Normal(normal) => { + let NormalAttr { item, tokens } = &mut **normal; + item.path.segments[0].ident.name = trace_name; + // This makes the trace attributes unobservable to token-based proc macros. + *tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::default())); + } + AttrKind::DocComment(..) => unreachable!(), + } + attr +} + #[macro_export] macro_rules! configure { ($this:ident, $node:ident) => { @@ -280,16 +293,7 @@ impl<'a> StripUnconfigured<'a> { // A trace attribute left in AST in place of the original `cfg_attr` attribute. // It can later be used by lints or other diagnostics. - let mut trace_attr = cfg_attr.clone(); - match &mut trace_attr.kind { - AttrKind::Normal(normal) => { - let NormalAttr { item, tokens } = &mut **normal; - item.path.segments[0].ident.name = sym::cfg_attr_trace; - // This makes the trace attributes unobservable to token-based proc macros. - *tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::default())); - } - AttrKind::DocComment(..) => unreachable!(), - } + let trace_attr = attr_into_trace(cfg_attr.clone(), sym::cfg_attr_trace); let Some((cfg_predicate, expanded_attrs)) = rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index d0bd8a89d9b..22da1179feb 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -33,7 +33,7 @@ use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, sym}; use smallvec::SmallVec; use crate::base::*; -use crate::config::StripUnconfigured; +use crate::config::{StripUnconfigured, attr_into_trace}; use crate::errors::{ EmptyDelegationMac, GlobDelegationOutsideImpls, GlobDelegationTraitlessQpath, IncompleteParse, RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, @@ -2003,7 +2003,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let attr_name = attr.ident().unwrap().name; // `#[cfg]` and `#[cfg_attr]` are special - they are // eagerly evaluated. - if attr_name != sym::cfg && attr_name != sym::cfg_attr_trace { + if attr_name != sym::cfg_trace && attr_name != sym::cfg_attr_trace { self.cx.sess.psess.buffer_lint( UNUSED_ATTRIBUTES, attr.span, @@ -2027,11 +2027,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ) -> (bool, Option<ast::MetaItem>) { let (res, meta_item) = self.cfg().cfg_true(&attr); if res { - // FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion, - // and some tools like rustdoc and clippy rely on that. Find a way to remove them - // while keeping the tools working. - self.cx.expanded_inert_attrs.mark(&attr); - node.visit_attrs(|attrs| attrs.insert(pos, attr)); + // A trace attribute left in AST in place of the original `cfg` attribute. + // It can later be used by lints or other diagnostics. + let trace_attr = attr_into_trace(attr, sym::cfg_trace); + node.visit_attrs(|attrs| attrs.insert(pos, trace_attr)); } (res, meta_item) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index fd936458f11..6fe65c88f71 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -760,10 +760,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(Word, List: r#""...""#), DuplicatesOk, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE ), - // Trace that is left when a `cfg_attr` attribute is expanded. - // The attribute is not gated, to avoid stability errors, but it cannot be used in stable or - // unstable code directly because `sym::cfg_attr_trace` is not a valid identifier, it can only - // be generated by the compiler. + // Traces that are left when `cfg` and `cfg_attr` attributes are expanded. + // The attributes are not gated, to avoid stability errors, but they cannot be used in stable + // or unstable code directly because `sym::cfg_(attr_)trace` are not valid identifiers, they + // can only be generated by the compiler. + ungated!( + cfg_trace, Normal, template!(Word /* irrelevant */), DuplicatesOk, + EncodeCrossCrate::No + ), ungated!( cfg_attr_trace, Normal, template!(Word /* irrelevant */), DuplicatesOk, EncodeCrossCrate::No diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 255f5fee52a..ba8124b11fc 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -25,12 +25,18 @@ use tracing::debug; struct Context { /// The scope that contains any new variables declared, plus its depth in /// the scope tree. - var_parent: Option<(Scope, ScopeDepth)>, + var_parent: Option<Scope>, /// Region parent of expressions, etc., plus its depth in the scope tree. parent: Option<(Scope, ScopeDepth)>, } +impl Context { + fn set_var_parent(&mut self) { + self.var_parent = self.parent.map(|(p, _)| p); + } +} + struct ScopeResolutionVisitor<'tcx> { tcx: TyCtxt<'tcx>, @@ -78,7 +84,7 @@ fn record_var_lifetime(visitor: &mut ScopeResolutionVisitor<'_>, var_id: hir::It // // extern fn isalnum(c: c_int) -> c_int } - Some((parent_scope, _)) => visitor.scope_tree.record_var_scope(var_id, parent_scope), + Some(parent_scope) => visitor.scope_tree.record_var_scope(var_id, parent_scope), } } @@ -113,7 +119,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi // itself has returned. visitor.enter_node_scope_with_dtor(blk.hir_id.local_id); - visitor.cx.var_parent = visitor.cx.parent; + visitor.cx.set_var_parent(); { // This block should be kept approximately in sync with @@ -132,7 +138,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi local_id: blk.hir_id.local_id, data: ScopeData::Remainder(FirstStatementIndex::new(i)), }); - visitor.cx.var_parent = visitor.cx.parent; + visitor.cx.set_var_parent(); visitor.visit_stmt(statement); // We need to back out temporarily to the last enclosing scope // for the `else` block, so that even the temporaries receiving @@ -157,7 +163,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi local_id: blk.hir_id.local_id, data: ScopeData::Remainder(FirstStatementIndex::new(i)), }); - visitor.cx.var_parent = visitor.cx.parent; + visitor.cx.set_var_parent(); visitor.visit_stmt(statement) } hir::StmtKind::Item(..) => { @@ -207,7 +213,7 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir: visitor.terminating_scopes.insert(arm.hir_id.local_id); visitor.enter_node_scope_with_dtor(arm.hir_id.local_id); - visitor.cx.var_parent = visitor.cx.parent; + visitor.cx.set_var_parent(); if let Some(expr) = arm.guard && !has_let_expr(expr) @@ -221,8 +227,6 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir: } fn resolve_pat<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) { - visitor.record_child_scope(Scope { local_id: pat.hir_id.local_id, data: ScopeData::Node }); - // If this is a binding then record the lifetime of that binding. if let PatKind::Binding(..) = pat.kind { record_var_lifetime(visitor, pat.hir_id.local_id); @@ -486,7 +490,7 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi ScopeData::IfThen }; visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data }); - visitor.cx.var_parent = visitor.cx.parent; + visitor.cx.set_var_parent(); visitor.visit_expr(cond); visitor.visit_expr(then); visitor.cx = expr_cx; @@ -501,7 +505,7 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi ScopeData::IfThen }; visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data }); - visitor.cx.var_parent = visitor.cx.parent; + visitor.cx.set_var_parent(); visitor.visit_expr(cond); visitor.visit_expr(then); visitor.cx = expr_cx; @@ -560,7 +564,7 @@ fn resolve_local<'tcx>( ) { debug!("resolve_local(pat={:?}, init={:?})", pat, init); - let blk_scope = visitor.cx.var_parent.map(|(p, _)| p); + let blk_scope = visitor.cx.var_parent; // As an exception to the normal rules governing temporary // lifetimes, initializers in a let have a temporary lifetime @@ -625,10 +629,7 @@ fn resolve_local<'tcx>( if is_binding_pat(pat) { visitor.scope_tree.record_rvalue_candidate( expr.hir_id, - RvalueCandidateType::Pattern { - target: expr.hir_id.local_id, - lifetime: blk_scope, - }, + RvalueCandidate { target: expr.hir_id.local_id, lifetime: blk_scope }, ); } } @@ -733,10 +734,7 @@ fn resolve_local<'tcx>( record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id); visitor.scope_tree.record_rvalue_candidate( subexpr.hir_id, - RvalueCandidateType::Borrow { - target: subexpr.hir_id.local_id, - lifetime: blk_id, - }, + RvalueCandidate { target: subexpr.hir_id.local_id, lifetime: blk_id }, ); } hir::ExprKind::Struct(_, fields, _) => { @@ -857,13 +855,12 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> { self.enter_body(body.value.hir_id, |this| { if this.tcx.hir_body_owner_kind(owner_id).is_fn_or_closure() { // The arguments and `self` are parented to the fn. - this.cx.var_parent = this.cx.parent.take(); + this.cx.set_var_parent(); for param in body.params { this.visit_pat(param.pat); } // The body of the every fn is a root scope. - this.cx.parent = this.cx.var_parent; this.visit_expr(body.value) } else { // Only functions have an outer terminating (drop) scope, while diff --git a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs index 98d7f777d6b..973dc7141e6 100644 --- a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs +++ b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs @@ -2,7 +2,7 @@ use hir::Node; use hir::def_id::DefId; use rustc_hir as hir; use rustc_middle::bug; -use rustc_middle::middle::region::{RvalueCandidateType, Scope, ScopeTree}; +use rustc_middle::middle::region::{RvalueCandidate, Scope, ScopeTree}; use rustc_middle::ty::RvalueScopes; use tracing::debug; @@ -55,15 +55,11 @@ fn record_rvalue_scope_rec( fn record_rvalue_scope( rvalue_scopes: &mut RvalueScopes, expr: &hir::Expr<'_>, - candidate: &RvalueCandidateType, + candidate: &RvalueCandidate, ) { debug!("resolve_rvalue_scope(expr={expr:?}, candidate={candidate:?})"); - match candidate { - RvalueCandidateType::Borrow { lifetime, .. } - | RvalueCandidateType::Pattern { lifetime, .. } => { - record_rvalue_scope_rec(rvalue_scopes, expr, *lifetime) - } // FIXME(@dingxiangfei2009): handle the candidates in the function call arguments - } + record_rvalue_scope_rec(rvalue_scopes, expr, candidate.lifetime) + // FIXME(@dingxiangfei2009): handle the candidates in the function call arguments } pub(crate) fn resolve_rvalue_scopes<'a, 'tcx>( diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 66861519e17..66ece8f0e52 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -224,7 +224,7 @@ pub struct ScopeTree { /// and not the enclosing *statement*. Expressions that are not present in this /// table are not rvalue candidates. The set of rvalue candidates is computed /// during type check based on a traversal of the AST. - pub rvalue_candidates: HirIdMap<RvalueCandidateType>, + pub rvalue_candidates: HirIdMap<RvalueCandidate>, /// Backwards incompatible scoping that will be introduced in future editions. /// This information is used later for linting to identify locals and @@ -308,15 +308,14 @@ pub struct ScopeTree { pub yield_in_scope: UnordMap<Scope, Vec<YieldData>>, } -/// Identifies the reason that a given expression is an rvalue candidate -/// (see the `rvalue_candidates` field for more information what rvalue -/// candidates in general). In constants, the `lifetime` field is None -/// to indicate that certain expressions escape into 'static and -/// should have no local cleanup scope. +/// See the `rvalue_candidates` field for more information on rvalue +/// candidates in general. +/// The `lifetime` field is None to indicate that certain expressions escape +/// into 'static and should have no local cleanup scope. #[derive(Debug, Copy, Clone, HashStable)] -pub enum RvalueCandidateType { - Borrow { target: hir::ItemLocalId, lifetime: Option<Scope> }, - Pattern { target: hir::ItemLocalId, lifetime: Option<Scope> }, +pub struct RvalueCandidate { + pub target: hir::ItemLocalId, + pub lifetime: Option<Scope>, } #[derive(Debug, Copy, Clone, HashStable)] @@ -344,16 +343,12 @@ impl ScopeTree { self.var_map.insert(var, lifetime); } - pub fn record_rvalue_candidate(&mut self, var: HirId, candidate_type: RvalueCandidateType) { - debug!("record_rvalue_candidate(var={var:?}, type={candidate_type:?})"); - match &candidate_type { - RvalueCandidateType::Borrow { lifetime: Some(lifetime), .. } - | RvalueCandidateType::Pattern { lifetime: Some(lifetime), .. } => { - assert!(var.local_id != lifetime.local_id) - } - _ => {} + pub fn record_rvalue_candidate(&mut self, var: HirId, candidate: RvalueCandidate) { + debug!("record_rvalue_candidate(var={var:?}, candidate={candidate:?})"); + if let Some(lifetime) = &candidate.lifetime { + assert!(var.local_id != lifetime.local_id) } - self.rvalue_candidates.insert(var, candidate_type); + self.rvalue_candidates.insert(var, candidate); } /// Returns the narrowest scope that encloses `id`, if any. diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 85d9db7ee74..d4cc562e70c 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -73,9 +73,7 @@ impl GenericParamDef { pub fn is_anonymous_lifetime(&self) -> bool { match self.kind { - GenericParamDefKind::Lifetime => { - self.name == kw::UnderscoreLifetime || self.name == kw::Empty - } + GenericParamDefKind::Lifetime => self.name == kw::UnderscoreLifetime, _ => false, } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6bdd0a0647d..ac98cbc8d6c 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -457,7 +457,7 @@ impl EarlyParamRegion { /// Does this early bound region have a name? Early bound regions normally /// always have names except when using anonymous lifetimes (`'_`). pub fn has_name(&self) -> bool { - self.name != kw::UnderscoreLifetime && self.name != kw::Empty + self.name != kw::UnderscoreLifetime } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 3ef8ecc59e4..3281cb4135a 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2591,11 +2591,9 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { // to fit that into a short string. Hence the recommendation to use // `explain_region()` or `note_and_explain_region()`. match *region { - ty::ReEarlyParam(ref data) => { - if data.name != kw::Empty { - p!(write("{}", data.name)); - return Ok(()); - } + ty::ReEarlyParam(data) => { + p!(write("{}", data.name)); + return Ok(()); } ty::ReLateParam(ty::LateParamRegion { kind, .. }) => { if let Some(name) = kind.get_name() { @@ -2834,7 +2832,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { (name, ty::BoundRegionKind::Named(CRATE_DEF_ID.to_def_id(), name)) } - ty::BoundRegionKind::Named(def_id, kw::UnderscoreLifetime | kw::Empty) => { + ty::BoundRegionKind::Named(def_id, kw::UnderscoreLifetime) => { let name = next_name(self); if let Some(lt_idx) = lifetime_idx { diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index fb52cf96b02..c78306f2ca3 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -400,9 +400,7 @@ impl LateParamRegionKind { pub fn is_named(&self) -> bool { match *self { - LateParamRegionKind::Named(_, name) => { - name != kw::UnderscoreLifetime && name != kw::Empty - } + LateParamRegionKind::Named(_, name) => name != kw::UnderscoreLifetime, _ => false, } } @@ -475,7 +473,7 @@ impl core::fmt::Debug for BoundRegion { impl BoundRegionKind { pub fn is_named(&self) -> bool { match *self { - BoundRegionKind::Named(_, name) => name != kw::UnderscoreLifetime && name != kw::Empty, + BoundRegionKind::Named(_, name) => name != kw::UnderscoreLifetime, _ => false, } } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 9ecde2a9eb5..6bbd650dcdf 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -16,7 +16,8 @@ use rustc_span::{Span, Symbol, sym}; use crate::{errors, parse_in}; pub fn check_attr(psess: &ParseSess, attr: &Attribute) { - if attr.is_doc_comment() || attr.has_name(sym::cfg_attr_trace) { + if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) + { return; } @@ -215,11 +216,7 @@ pub fn check_builtin_meta_item( template: AttributeTemplate, deny_unsafety: bool, ) { - // Some special attributes like `cfg` must be checked - // before the generic check, so we skip them here. - let should_skip = |name| name == sym::cfg; - - if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) { + if !is_attr_template_compatible(&template, &meta.kind) { emit_malformed_attribute(psess, style, meta.span, name, template); } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9238c73cdb1..1e1fb42a48f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -272,6 +272,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::forbid | sym::cfg | sym::cfg_attr + | sym::cfg_trace | sym::cfg_attr_trace // need to be fixed | sym::cfi_encoding // FIXME(cfi_encoding) @@ -574,8 +575,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains accurate const ALLOW_LIST: &[rustc_span::Symbol] = &[ // conditional compilation - sym::cfg, - sym::cfg_attr, + sym::cfg_trace, sym::cfg_attr_trace, // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`) sym::test, @@ -2656,7 +2656,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { // only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed // if we allow more attributes (e.g., tool attributes and `allow/deny/warn`) // in where clauses. After that, only `self.check_attributes` should be enough. - const ATTRS_ALLOWED: &[Symbol] = &[sym::cfg, sym::cfg_attr, sym::cfg_attr_trace]; + const ATTRS_ALLOWED: &[Symbol] = &[sym::cfg_trace, sym::cfg_attr_trace]; let spans = self .tcx .hir_attrs(where_predicate.hir_id) diff --git a/compiler/rustc_query_system/src/ich/mod.rs b/compiler/rustc_query_system/src/ich/mod.rs index 852d93b711f..25778add60a 100644 --- a/compiler/rustc_query_system/src/ich/mod.rs +++ b/compiler/rustc_query_system/src/ich/mod.rs @@ -8,7 +8,7 @@ mod hcx; mod impls_syntax; pub const IGNORED_ATTRIBUTES: &[Symbol] = &[ - sym::cfg, + sym::cfg_trace, // FIXME should this really be ignored? sym::rustc_if_this_changed, sym::rustc_then_this_would_need, sym::rustc_dirty, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index cf8db2267f4..e40f84e7e59 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1665,41 +1665,81 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // the struct literal syntax at all, as that will cause a subsequent error. let fields = this.r.field_idents(def_id); let has_fields = fields.as_ref().is_some_and(|f| !f.is_empty()); - let (fields, applicability) = match fields { - Some(fields) => { - let fields = if let Some(old_fields) = old_fields { - fields - .iter() - .enumerate() - .map(|(idx, new)| (new, old_fields.get(idx))) - .map(|(new, old)| { - if let Some(Some(old)) = old - && new.as_str() != old - { - format!("{new}: {old}") - } else { - new.to_string() - } - }) - .collect::<Vec<String>>() - } else { - fields - .iter() - .map(|f| format!("{f}{tail}")) - .collect::<Vec<String>>() - }; - - (fields.join(", "), applicability) - } - None => ("/* fields */".to_string(), Applicability::HasPlaceholders), - }; - let pad = if has_fields { " " } else { "" }; - err.span_suggestion( + + if let PathSource::Expr(Some(Expr { + kind: ExprKind::Call(path, args), span, - format!("use struct {descr} syntax instead"), - format!("{path_str} {{{pad}{fields}{pad}}}"), - applicability, - ); + .. + })) = source + && !args.is_empty() + && let Some(fields) = &fields + && args.len() == fields.len() + // Make sure we have same number of args as fields + { + let path_span = path.span; + let mut parts = Vec::new(); + + // Start with the opening brace + parts.push(( + path_span.shrink_to_hi().until(args[0].span), + "{".to_owned(), + )); + + for (field, arg) in fields.iter().zip(args.iter()) { + // Add the field name before the argument + parts.push((arg.span.shrink_to_lo(), format!("{}: ", field))); + } + + // Add the closing brace + parts.push(( + args.last().unwrap().span.shrink_to_hi().until(span.shrink_to_hi()), + "}".to_owned(), + )); + + err.multipart_suggestion_verbose( + format!("use struct {descr} syntax instead of calling"), + parts, + applicability, + ); + } else { + let (fields, applicability) = match fields { + Some(fields) => { + let fields = if let Some(old_fields) = old_fields { + fields + .iter() + .enumerate() + .map(|(idx, new)| (new, old_fields.get(idx))) + .map(|(new, old)| { + if let Some(Some(old)) = old + && new.as_str() != old + { + format!("{new}: {old}") + } else { + new.to_string() + } + }) + .collect::<Vec<String>>() + } else { + fields + .iter() + .map(|f| format!("{f}{tail}")) + .collect::<Vec<String>>() + }; + + (fields.join(", "), applicability) + } + None => { + ("/* fields */".to_string(), Applicability::HasPlaceholders) + } + }; + let pad = if has_fields { " " } else { "" }; + err.span_suggestion( + span, + format!("use struct {descr} syntax instead"), + format!("{path_str} {{{pad}{fields}{pad}}}"), + applicability, + ); + } } if let PathSource::Expr(Some(Expr { kind: ExprKind::Call(path, args), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 555764c26a6..6807f96e712 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -623,6 +623,7 @@ symbols! { cfg_target_has_atomic_equal_alignment, cfg_target_thread_local, cfg_target_vendor, + cfg_trace: "<cfg>", // must not be a valid identifier cfg_ub_checks, cfg_version, cfi, diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 8521cf4162f..c7852464922 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -74,7 +74,6 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res let ours; let mut name; let mut tries = 0; - let mut reject_remote_clients_flag = c::PIPE_REJECT_REMOTE_CLIENTS; loop { tries += 1; name = format!( @@ -96,7 +95,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res c::PIPE_TYPE_BYTE | c::PIPE_READMODE_BYTE | c::PIPE_WAIT - | reject_remote_clients_flag, + | c::PIPE_REJECT_REMOTE_CLIENTS, 1, PIPE_BUFFER_CAPACITY, PIPE_BUFFER_CAPACITY, @@ -112,30 +111,15 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res // // Don't try again too much though as this could also perhaps be a // legit error. - // If `ERROR_INVALID_PARAMETER` is returned, this probably means we're - // running on pre-Vista version where `PIPE_REJECT_REMOTE_CLIENTS` is - // not supported, so we continue retrying without it. This implies - // reduced security on Windows versions older than Vista by allowing - // connections to this pipe from remote machines. - // Proper fix would increase the number of FFI imports and introduce - // significant amount of Windows XP specific code with no clean - // testing strategy - // For more info, see https://github.com/rust-lang/rust/pull/37677. if handle == c::INVALID_HANDLE_VALUE { let error = api::get_last_error(); - if tries < 10 { - if error == WinError::ACCESS_DENIED { - continue; - } else if reject_remote_clients_flag != 0 - && error == WinError::INVALID_PARAMETER - { - reject_remote_clients_flag = 0; - tries -= 1; - continue; - } + if tries < 10 && error == WinError::ACCESS_DENIED { + continue; + } else { + return Err(io::Error::from_raw_os_error(error.code as i32)); } - return Err(io::Error::from_raw_os_error(error.code as i32)); } + ours = Handle::from_raw_handle(handle); break; } diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index 99b5ad9cb9f..98f471ad54b 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -113,18 +113,23 @@ where pub(super) fn run_spawn_hooks(thread: &Thread) -> ChildSpawnHooks { // Get a snapshot of the spawn hooks. // (Increments the refcount to the first node.) - let hooks = SPAWN_HOOKS.with(|hooks| { + if let Ok(hooks) = SPAWN_HOOKS.try_with(|hooks| { let snapshot = hooks.take(); hooks.set(snapshot.clone()); snapshot - }); - // Iterate over the hooks, run them, and collect the results in a vector. - let to_run: Vec<_> = iter::successors(hooks.first.as_deref(), |hook| hook.next.as_deref()) - .map(|hook| (hook.hook)(thread)) - .collect(); - // Pass on the snapshot of the hooks and the results to the new thread, - // which will then run SpawnHookResults::run(). - ChildSpawnHooks { hooks, to_run } + }) { + // Iterate over the hooks, run them, and collect the results in a vector. + let to_run: Vec<_> = iter::successors(hooks.first.as_deref(), |hook| hook.next.as_deref()) + .map(|hook| (hook.hook)(thread)) + .collect(); + // Pass on the snapshot of the hooks and the results to the new thread, + // which will then run SpawnHookResults::run(). + ChildSpawnHooks { hooks, to_run } + } else { + // TLS has been destroyed. Skip running the hooks. + // See https://github.com/rust-lang/rust/issues/138696 + ChildSpawnHooks::default() + } } /// The results of running the spawn hooks. diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 068e237c2cd..6d63709f474 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -24,31 +24,11 @@ pub(crate) fn parse(config: &str) -> Config { #[test] fn download_ci_llvm() { - let config = parse(""); - let is_available = llvm::is_ci_llvm_available_for_target(&config, config.llvm_assertions); - if is_available { - assert!(config.llvm_from_ci); - } - - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_string(), - "--config=/does/not/exist".to_string(), - "--ci".to_string(), - "false".to_string(), - ]), - |&_| toml::from_str("llvm.download-ci-llvm = true"), - ); - let is_available = llvm::is_ci_llvm_available_for_target(&config, config.llvm_assertions); - if is_available { - assert!(config.llvm_from_ci); - } - let config = parse("llvm.download-ci-llvm = false"); assert!(!config.llvm_from_ci); let if_unchanged_config = parse("llvm.download-ci-llvm = \"if-unchanged\""); - if if_unchanged_config.llvm_from_ci { + if if_unchanged_config.llvm_from_ci && if_unchanged_config.is_running_on_ci { let has_changes = if_unchanged_config .last_modified_commit(LLVM_INVALIDATION_PATHS, "download-ci-llvm", true) .is_none(); diff --git a/src/bootstrap/src/utils/metrics.rs b/src/bootstrap/src/utils/metrics.rs index 885fff9c32c..862c4449624 100644 --- a/src/bootstrap/src/utils/metrics.rs +++ b/src/bootstrap/src/utils/metrics.rs @@ -9,9 +9,10 @@ use std::fs::File; use std::io::BufWriter; use std::time::{Duration, Instant, SystemTime}; +use build_helper::ci::CiEnv; use build_helper::metrics::{ - JsonInvocation, JsonInvocationSystemStats, JsonNode, JsonRoot, JsonStepSystemStats, Test, - TestOutcome, TestSuite, TestSuiteMetadata, + CiMetadata, JsonInvocation, JsonInvocationSystemStats, JsonNode, JsonRoot, JsonStepSystemStats, + Test, TestOutcome, TestSuite, TestSuiteMetadata, }; use sysinfo::{CpuRefreshKind, RefreshKind, System}; @@ -217,7 +218,12 @@ impl BuildMetrics { children: steps.into_iter().map(|step| self.prepare_json_step(step)).collect(), }); - let json = JsonRoot { format_version: CURRENT_FORMAT_VERSION, system_stats, invocations }; + let json = JsonRoot { + format_version: CURRENT_FORMAT_VERSION, + system_stats, + invocations, + ci_metadata: get_ci_metadata(CiEnv::current()), + }; t!(std::fs::create_dir_all(dest.parent().unwrap())); let mut file = BufWriter::new(t!(File::create(&dest))); @@ -245,6 +251,16 @@ impl BuildMetrics { } } +fn get_ci_metadata(ci_env: CiEnv) -> Option<CiMetadata> { + if ci_env != CiEnv::GitHubActions { + return None; + } + let workflow_run_id = + std::env::var("GITHUB_WORKFLOW_RUN_ID").ok().and_then(|id| id.parse::<u64>().ok())?; + let repository = std::env::var("GITHUB_REPOSITORY").ok()?; + Some(CiMetadata { workflow_run_id, repository }) +} + struct MetricsState { finished_steps: Vec<StepMetrics>, running_steps: Vec<StepMetrics>, diff --git a/src/build_helper/src/metrics.rs b/src/build_helper/src/metrics.rs index fdff9cd18ce..8b82e62a327 100644 --- a/src/build_helper/src/metrics.rs +++ b/src/build_helper/src/metrics.rs @@ -9,6 +9,19 @@ pub struct JsonRoot { pub format_version: usize, pub system_stats: JsonInvocationSystemStats, pub invocations: Vec<JsonInvocation>, + #[serde(default)] + pub ci_metadata: Option<CiMetadata>, +} + +/// Represents metadata about bootstrap's execution in CI. +#[derive(Serialize, Deserialize)] +pub struct CiMetadata { + /// GitHub run ID of the workflow where bootstrap was executed. + /// Note that the run ID will be shared amongst all jobs executed in that workflow. + pub workflow_run_id: u64, + /// Full name of a GitHub repository where bootstrap was executed in CI. + /// e.g. `rust-lang-ci/rust`. + pub repository: String, } #[derive(Serialize, Deserialize)] diff --git a/src/ci/citool/src/analysis.rs b/src/ci/citool/src/analysis.rs index 2b001f28b0e..7fbfad467c6 100644 --- a/src/ci/citool/src/analysis.rs +++ b/src/ci/citool/src/analysis.rs @@ -1,5 +1,6 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::fmt::Debug; +use std::time::Duration; use build_helper::metrics::{ BuildStep, JsonRoot, TestOutcome, TestSuite, TestSuiteMetadata, escape_step_name, @@ -184,11 +185,70 @@ fn render_table(suites: BTreeMap<String, TestSuiteRecord>) -> String { } /// Outputs a report of test differences between the `parent` and `current` commits. -pub fn output_test_diffs(job_metrics: HashMap<JobName, JobMetrics>) { +pub fn output_test_diffs(job_metrics: &HashMap<JobName, JobMetrics>) { let aggregated_test_diffs = aggregate_test_diffs(&job_metrics); report_test_diffs(aggregated_test_diffs); } +/// Prints the ten largest differences in bootstrap durations. +pub fn output_largest_duration_changes(job_metrics: &HashMap<JobName, JobMetrics>) { + struct Entry<'a> { + job: &'a JobName, + before: Duration, + after: Duration, + change: f64, + } + + let mut changes: Vec<Entry> = vec![]; + for (job, metrics) in job_metrics { + if let Some(parent) = &metrics.parent { + let duration_before = parent + .invocations + .iter() + .map(|i| BuildStep::from_invocation(i).duration) + .sum::<Duration>(); + let duration_after = metrics + .current + .invocations + .iter() + .map(|i| BuildStep::from_invocation(i).duration) + .sum::<Duration>(); + let pct_change = duration_after.as_secs_f64() / duration_before.as_secs_f64(); + let pct_change = pct_change * 100.0; + // Normalize around 100, to get + for regression and - for improvements + let pct_change = pct_change - 100.0; + changes.push(Entry { + job, + before: duration_before, + after: duration_after, + change: pct_change, + }); + } + } + changes.sort_by(|e1, e2| e1.change.partial_cmp(&e2.change).unwrap().reverse()); + + println!("# Job duration changes"); + for (index, entry) in changes.into_iter().take(10).enumerate() { + println!( + "{}. `{}`: {:.1}s -> {:.1}s ({:.1}%)", + index + 1, + entry.job, + entry.before.as_secs_f64(), + entry.after.as_secs_f64(), + entry.change + ); + } + + println!(); + output_details("How to interpret the job duration changes?", || { + println!( + r#"Job durations can vary a lot, based on the actual runner instance +that executed the job, system noise, invalidated caches, etc. The table above is provided +mostly for t-infra members, for simpler debugging of potential CI slow-downs."# + ); + }); +} + #[derive(Default)] struct TestSuiteRecord { passed: u64, diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index 5f5c50dc43a..6db5eab458c 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -15,7 +15,7 @@ use clap::Parser; use jobs::JobDatabase; use serde_yaml::Value; -use crate::analysis::output_test_diffs; +use crate::analysis::{output_largest_duration_changes, output_test_diffs}; use crate::cpu_usage::load_cpu_usage; use crate::datadog::upload_datadog_metric; use crate::jobs::RunType; @@ -160,7 +160,7 @@ fn postprocess_metrics( job_name, JobMetrics { parent: Some(parent_metrics), current: metrics }, )]); - output_test_diffs(job_metrics); + output_test_diffs(&job_metrics); return Ok(()); } Err(error) => { @@ -180,7 +180,8 @@ fn post_merge_report(db: JobDatabase, current: String, parent: String) -> anyhow let metrics = download_auto_job_metrics(&db, &parent, ¤t)?; println!("\nComparing {parent} (parent) -> {current} (this PR)\n"); - output_test_diffs(metrics); + output_test_diffs(&metrics); + output_largest_duration_changes(&metrics); Ok(()) } diff --git a/src/ci/citool/src/metrics.rs b/src/ci/citool/src/metrics.rs index 086aa5009f3..a816fb3c4f1 100644 --- a/src/ci/citool/src/metrics.rs +++ b/src/ci/citool/src/metrics.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::path::Path; +use std::path::{Path, PathBuf}; use anyhow::Context; use build_helper::metrics::{JsonNode, JsonRoot, TestSuite}; @@ -74,6 +74,17 @@ Maybe it was newly added?"#, } pub fn download_job_metrics(job_name: &str, sha: &str) -> anyhow::Result<JsonRoot> { + // Best effort cache to speed-up local re-executions of citool + let cache_path = PathBuf::from(".citool-cache").join(sha).join(format!("{job_name}.json")); + if cache_path.is_file() { + if let Ok(metrics) = std::fs::read_to_string(&cache_path) + .map_err(|err| err.into()) + .and_then(|data| anyhow::Ok::<JsonRoot>(serde_json::from_str::<JsonRoot>(&data)?)) + { + return Ok(metrics); + } + } + let url = get_metrics_url(job_name, sha); let mut response = ureq::get(&url).call()?; if !response.status().is_success() { @@ -87,6 +98,13 @@ pub fn download_job_metrics(job_name: &str, sha: &str) -> anyhow::Result<JsonRoo .body_mut() .read_json() .with_context(|| anyhow::anyhow!("cannot deserialize metrics from {url}"))?; + + if let Ok(_) = std::fs::create_dir_all(cache_path.parent().unwrap()) { + if let Ok(data) = serde_json::to_string(&data) { + let _ = std::fs::write(cache_path, data); + } + } + Ok(data) } diff --git a/src/ci/citool/src/utils.rs b/src/ci/citool/src/utils.rs index b9b1bf4d455..a4c6ff85ef7 100644 --- a/src/ci/citool/src/utils.rs +++ b/src/ci/citool/src/utils.rs @@ -23,7 +23,6 @@ where println!( r"<details> <summary>{summary}</summary> - " ); func(); diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 2805bb1118d..00d791eeb6b 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -355,6 +355,8 @@ docker \ --env GITHUB_ACTIONS \ --env GITHUB_REF \ --env GITHUB_STEP_SUMMARY="/checkout/obj/${SUMMARY_FILE}" \ + --env GITHUB_WORKFLOW_RUN_ID \ + --env GITHUB_REPOSITORY \ --env RUST_BACKTRACE \ --env TOOLSTATE_REPO_ACCESS_TOKEN \ --env TOOLSTATE_REPO \ diff --git a/src/doc/rustc/src/platform-support/apple-darwin.md b/src/doc/rustc/src/platform-support/apple-darwin.md index dba2c4b2aaf..22c54d04b1e 100644 --- a/src/doc/rustc/src/platform-support/apple-darwin.md +++ b/src/doc/rustc/src/platform-support/apple-darwin.md @@ -70,4 +70,5 @@ the `-mmacosx-version-min=...`, `-miphoneos-version-min=...` or similar flags to disambiguate. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk macosx --show-sdk-path`. diff --git a/src/doc/rustc/src/platform-support/apple-ios-macabi.md b/src/doc/rustc/src/platform-support/apple-ios-macabi.md index a54656190d1..79966d908d8 100644 --- a/src/doc/rustc/src/platform-support/apple-ios-macabi.md +++ b/src/doc/rustc/src/platform-support/apple-ios-macabi.md @@ -20,7 +20,8 @@ These targets are cross-compiled, and require the corresponding macOS SDK iOS-specific headers, as provided by Xcode 11 or higher. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk macosx --show-sdk-path`. ### OS version diff --git a/src/doc/rustc/src/platform-support/apple-ios.md b/src/doc/rustc/src/platform-support/apple-ios.md index cfb458fdb73..7f5dc361c49 100644 --- a/src/doc/rustc/src/platform-support/apple-ios.md +++ b/src/doc/rustc/src/platform-support/apple-ios.md @@ -26,7 +26,8 @@ These targets are cross-compiled, and require the corresponding iOS SDK ARM64 targets, Xcode 12 or higher is required. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk iphoneos --show-sdk-path`. ### OS version diff --git a/src/doc/rustc/src/platform-support/apple-tvos.md b/src/doc/rustc/src/platform-support/apple-tvos.md index 166bb1b6db2..fc46db20074 100644 --- a/src/doc/rustc/src/platform-support/apple-tvos.md +++ b/src/doc/rustc/src/platform-support/apple-tvos.md @@ -20,7 +20,8 @@ These targets are cross-compiled, and require the corresponding tvOS SDK ARM64 targets, Xcode 12 or higher is required. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk appletvos --show-sdk-path`. ### OS version diff --git a/src/doc/rustc/src/platform-support/apple-visionos.md b/src/doc/rustc/src/platform-support/apple-visionos.md index a7bbae168a4..7cf9549227d 100644 --- a/src/doc/rustc/src/platform-support/apple-visionos.md +++ b/src/doc/rustc/src/platform-support/apple-visionos.md @@ -18,7 +18,8 @@ These targets are cross-compiled, and require the corresponding visionOS SDK (`XROS.sdk` or `XRSimulator.sdk`), as provided by Xcode 15 or newer. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk xros --show-sdk-path`. ### OS version diff --git a/src/doc/rustc/src/platform-support/apple-watchos.md b/src/doc/rustc/src/platform-support/apple-watchos.md index 0bf8cdf3614..7b12d9ebfd4 100644 --- a/src/doc/rustc/src/platform-support/apple-watchos.md +++ b/src/doc/rustc/src/platform-support/apple-watchos.md @@ -24,7 +24,8 @@ These targets are cross-compiled, and require the corresponding watchOS SDK ARM64 targets, Xcode 12 or higher is required. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk watchos --show-sdk-path`. ### OS version diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 6ace626fdcd..a48f5c623cd 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -114,8 +114,8 @@ fn synthesize_auto_trait_impl<'tcx>( }; Some(clean::Item { - name: None, inner: Box::new(clean::ItemInner { + name: None, attrs: Default::default(), stability: None, kind: clean::ImplItem(Box::new(clean::Impl { @@ -127,10 +127,10 @@ fn synthesize_auto_trait_impl<'tcx>( polarity, kind: clean::ImplKind::Auto, })), + item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id }, + cfg: None, + inline_stmt_id: None, }), - item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id }, - cfg: None, - inline_stmt_id: None, }) } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index a6d9676dd84..89245fee515 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -83,9 +83,9 @@ pub(crate) fn synthesize_blanket_impls( cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id)); blanket_impls.push(clean::Item { - name: None, - item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, inner: Box::new(clean::ItemInner { + name: None, + item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, attrs: Default::default(), stability: None, kind: clean::ImplItem(Box::new(clean::Impl { @@ -122,9 +122,9 @@ pub(crate) fn synthesize_blanket_impls( None, ))), })), + cfg: None, + inline_stmt_id: None, }), - cfg: None, - inline_stmt_id: None, }); } } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 4fd669ab6d1..e0e09b53fc2 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -151,7 +151,7 @@ pub(crate) fn try_inline( let mut item = crate::clean::generate_item_with_correct_attrs(cx, kind, did, name, import_def_id, None); // The visibility needs to reflect the one from the reexport and not from the "source" DefId. - item.inline_stmt_id = import_def_id; + item.inner.inline_stmt_id = import_def_id; ret.push(item); Some(ret) } @@ -655,11 +655,11 @@ fn build_module_items( // Primitive types can't be inlined so generate an import instead. let prim_ty = clean::PrimitiveType::from(p); items.push(clean::Item { - name: None, - // We can use the item's `DefId` directly since the only information ever used - // from it is `DefId.krate`. - item_id: ItemId::DefId(did), inner: Box::new(clean::ItemInner { + name: None, + // We can use the item's `DefId` directly since the only information ever + // used from it is `DefId.krate`. + item_id: ItemId::DefId(did), attrs: Default::default(), stability: None, kind: clean::ImportItem(clean::Import::new_simple( @@ -679,9 +679,9 @@ fn build_module_items( }, true, )), + cfg: None, + inline_stmt_id: None, }), - cfg: None, - inline_stmt_id: None, }); } else if let Some(i) = try_inline(cx, res, item.ident.name, attrs, visited) { items.extend(i) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index de6dc088176..c08ae168d69 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -210,7 +210,7 @@ fn generate_item_with_correct_attrs( let name = renamed.or(Some(name)); let mut item = Item::from_def_id_and_attrs_and_parts(def_id, name, kind, attrs, cfg); - item.inline_stmt_id = import_id; + item.inner.inline_stmt_id = import_id; item } @@ -1943,14 +1943,11 @@ fn clean_trait_object_lifetime_bound<'tcx>( // latter contrary to `clean_middle_region`. match *region { ty::ReStatic => Some(Lifetime::statik()), - ty::ReEarlyParam(region) if region.name != kw::Empty => Some(Lifetime(region.name)), - ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(_, name), .. }) - if name != kw::Empty => - { + ty::ReEarlyParam(region) => Some(Lifetime(region.name)), + ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(_, name), .. }) => { Some(Lifetime(name)) } - ty::ReEarlyParam(_) - | ty::ReBound(..) + ty::ReBound(..) | ty::ReLateParam(_) | ty::ReVar(_) | ty::RePlaceholder(_) @@ -2773,7 +2770,7 @@ fn add_without_unwanted_attributes<'hir>( if ident == sym::doc { filter_doc_attr(&mut normal.args, is_inline); attrs.push((Cow::Owned(attr), import_parent)); - } else if is_inline || ident != sym::cfg { + } else if is_inline || ident != sym::cfg_trace { // If it's not a `cfg()` attribute, we keep it. attrs.push((Cow::Owned(attr), import_parent)); } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 27eb56a9858..143191a6f2a 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -311,26 +311,31 @@ pub(crate) enum ExternalLocation { /// directly to the AST's concept of an item; it's a strict superset. #[derive(Clone)] pub(crate) struct Item { - /// The name of this item. - /// Optional because not every item has a name, e.g. impls. - pub(crate) name: Option<Symbol>, pub(crate) inner: Box<ItemInner>, - pub(crate) item_id: ItemId, - /// This is the `LocalDefId` of the `use` statement if the item was inlined. - /// The crate metadata doesn't hold this information, so the `use` statement - /// always belongs to the current crate. - pub(crate) inline_stmt_id: Option<LocalDefId>, - pub(crate) cfg: Option<Arc<Cfg>>, } +// Why does the `Item`/`ItemInner` split exist? `Vec<Item>`s are common, and +// without the split `Item` would be a large type (100+ bytes) which results in +// lots of wasted space in the unused parts of a `Vec<Item>`. With the split, +// `Item` is just 8 bytes, and the wasted space is avoided, at the cost of an +// extra allocation per item. This is a performance win. #[derive(Clone)] pub(crate) struct ItemInner { + /// The name of this item. + /// Optional because not every item has a name, e.g. impls. + pub(crate) name: Option<Symbol>, /// Information about this item that is specific to what kind of item it is. /// E.g., struct vs enum vs function. pub(crate) kind: ItemKind, pub(crate) attrs: Attributes, /// The effective stability, filled out by the `propagate-stability` pass. pub(crate) stability: Option<Stability>, + pub(crate) item_id: ItemId, + /// This is the `LocalDefId` of the `use` statement if the item was inlined. + /// The crate metadata doesn't hold this information, so the `use` statement + /// always belongs to the current crate. + pub(crate) inline_stmt_id: Option<LocalDefId>, + pub(crate) cfg: Option<Arc<Cfg>>, } impl std::ops::Deref for Item { @@ -488,11 +493,15 @@ impl Item { trace!("name={name:?}, def_id={def_id:?} cfg={cfg:?}"); Item { - item_id: def_id.into(), - inner: Box::new(ItemInner { kind, attrs, stability: None }), - name, - cfg, - inline_stmt_id: None, + inner: Box::new(ItemInner { + item_id: def_id.into(), + kind, + attrs, + stability: None, + name, + cfg, + inline_stmt_id: None, + }), } } @@ -1059,7 +1068,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> // `doc(cfg())` overrides `cfg()`). attrs .clone() - .filter(|attr| attr.has_name(sym::cfg)) + .filter(|attr| attr.has_name(sym::cfg_trace)) .filter_map(|attr| single(attr.meta_item_list()?)) .filter_map(|attr| Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten()) .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) @@ -2622,13 +2631,14 @@ mod size_asserts { use super::*; // tidy-alphabetical-start - static_assert_size!(Crate, 56); // frequently moved by-value + static_assert_size!(Crate, 16); // frequently moved by-value static_assert_size!(DocFragment, 32); static_assert_size!(GenericArg, 32); static_assert_size!(GenericArgs, 24); static_assert_size!(GenericParamDef, 40); static_assert_size!(Generics, 16); - static_assert_size!(Item, 48); + static_assert_size!(Item, 8); + static_assert_size!(ItemInner, 136); static_assert_size!(ItemKind, 48); static_assert_size!(PathSegment, 32); static_assert_size!(Type, 32); diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index e74fd67fbda..19402004ed5 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -385,7 +385,6 @@ impl DocFolder for CacheBuilder<'_, '_> { // implementations elsewhere. let ret = if let clean::Item { inner: box clean::ItemInner { kind: clean::ImplItem(ref i), .. }, - .. } = item { // Figure out the id of this impl. This may map to a diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index a5351b350dd..9d8eb70fbe0 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -43,7 +43,7 @@ impl JsonRenderer<'_> { let attrs = item.attributes(self.tcx, self.cache(), true); let span = item.span(self.tcx); let visibility = item.visibility(self.tcx); - let clean::Item { name, item_id, .. } = item; + let clean::ItemInner { name, item_id, .. } = *item.inner; let id = self.id_from_item(&item); let inner = match item.kind { clean::KeywordItem => return None, diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 572c9bf7552..eddafa9ba8e 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -61,7 +61,7 @@ impl CfgPropagator<'_, '_> { let (_, cfg) = merge_attrs(self.cx, item.attrs.other_attrs.as_slice(), Some((&attrs, None))); - item.cfg = cfg; + item.inner.cfg = cfg; } } @@ -71,7 +71,7 @@ impl DocFolder for CfgPropagator<'_, '_> { self.merge_with_parent_attributes(&mut item); - let new_cfg = match (self.parent_cfg.take(), item.cfg.take()) { + let new_cfg = match (self.parent_cfg.take(), item.inner.cfg.take()) { (None, None) => None, (Some(rc), None) | (None, Some(rc)) => Some(rc), (Some(mut a), Some(b)) => { @@ -81,7 +81,7 @@ impl DocFolder for CfgPropagator<'_, '_> { } }; self.parent_cfg = new_cfg.clone(); - item.cfg = new_cfg; + item.inner.cfg = new_cfg; let old_parent = if let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) { diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs index 5c486eb90cc..4c84e61b1f2 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs @@ -37,7 +37,6 @@ fn check_duplicated_attr( let Some(ident) = attr.ident() else { return }; let name = ident.name; if name == sym::doc - || name == sym::cfg_attr || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason { @@ -47,7 +46,7 @@ fn check_duplicated_attr( return; } if let Some(direct_parent) = parent.last() - && ["cfg", "cfg_attr"].contains(&direct_parent.as_str()) + && direct_parent == sym::cfg_trace.as_str() && [sym::all, sym::not, sym::any].contains(&name) { // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one diff --git a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs index 84136a2e6c2..7590fe96fd2 100644 --- a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs +++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs @@ -32,7 +32,7 @@ declare_lint_pass!(CfgNotTest => [CFG_NOT_TEST]); impl EarlyLintPass for CfgNotTest { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) { - if attr.has_name(rustc_span::sym::cfg) && contains_not_test(attr.meta_item_list().as_deref(), false) { + if attr.has_name(rustc_span::sym::cfg_trace) && contains_not_test(attr.meta_item_list().as_deref(), false) { span_lint_and_then( cx, CFG_NOT_TEST, diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs index 7c190e123b7..4c81b22861b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs @@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool { cx.tcx .hir_parent_id_iter(id) - .any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg))) + .any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg_trace))) } /// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 1307ff79bc5..668b0cb69e2 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -2629,7 +2629,7 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir> pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { if let Res::Def(_, def_id) = path.res { - return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr); + return cx.tcx.has_attr(def_id, sym::cfg_trace) || cx.tcx.has_attr(def_id, sym::cfg_attr); } } false @@ -2699,7 +2699,7 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool { /// use [`is_in_cfg_test`] pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool { tcx.hir_attrs(id).iter().any(|attr| { - if attr.has_name(sym::cfg) + if attr.has_name(sym::cfg_trace) && let Some(items) = attr.meta_item_list() && let [item] = &*items && item.has_name(sym::test) @@ -2723,11 +2723,11 @@ pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { /// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied. pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - tcx.has_attr(def_id, sym::cfg) + tcx.has_attr(def_id, sym::cfg_trace) || tcx .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id)) .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)) - .any(|attr| attr.has_name(sym::cfg)) + .any(|attr| attr.has_name(sym::cfg_trace)) } /// Walks up the HIR tree from the given expression in an attempt to find where the value is diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 3ec984edacb..950566b2582 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -22,6 +22,7 @@ pub mod util; use core::panic; use std::collections::HashSet; use std::ffi::OsString; +use std::fmt::Write; use std::io::{self, ErrorKind}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; @@ -570,18 +571,22 @@ pub fn run_tests(config: Arc<Config>) { // easy to miss which tests failed, and as such fail to reproduce // the failure locally. - println!( - "Some tests failed in compiletest suite={}{} mode={} host={} target={}", - config.suite, - config - .compare_mode - .as_ref() - .map(|c| format!(" compare_mode={:?}", c)) - .unwrap_or_default(), - config.mode, - config.host, - config.target - ); + let mut msg = String::from("Some tests failed in compiletest"); + write!(msg, " suite={}", config.suite).unwrap(); + + if let Some(compare_mode) = config.compare_mode.as_ref() { + write!(msg, " compare_mode={}", compare_mode).unwrap(); + } + + if let Some(pass_mode) = config.force_pass_mode.as_ref() { + write!(msg, " pass_mode={}", pass_mode).unwrap(); + } + + write!(msg, " mode={}", config.mode).unwrap(); + write!(msg, " host={}", config.host).unwrap(); + write!(msg, " target={}", config.target).unwrap(); + + println!("{msg}"); std::process::exit(1); } diff --git a/tests/pretty/tests-are-sorted.pp b/tests/pretty/tests-are-sorted.pp index 31449b51dc3..d6a2c0ff979 100644 --- a/tests/pretty/tests-are-sorted.pp +++ b/tests/pretty/tests-are-sorted.pp @@ -10,7 +10,6 @@ extern crate std; //@ pp-exact:tests-are-sorted.pp extern crate test; -#[cfg(test)] #[rustc_test_marker = "m_test"] #[doc(hidden)] pub const m_test: test::TestDescAndFn = @@ -35,7 +34,6 @@ pub const m_test: test::TestDescAndFn = fn m_test() {} extern crate test; -#[cfg(test)] #[rustc_test_marker = "z_test"] #[doc(hidden)] pub const z_test: test::TestDescAndFn = @@ -61,7 +59,6 @@ pub const z_test: test::TestDescAndFn = fn z_test() {} extern crate test; -#[cfg(test)] #[rustc_test_marker = "a_test"] #[doc(hidden)] pub const a_test: test::TestDescAndFn = diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs index 9a041557c7c..416145a0c15 100644 --- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs +++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs @@ -29,7 +29,6 @@ macro_rules! generate_s10 { ($expr: expr) => { #[cfg(feature = $expr)] //~^ ERROR expected unsuffixed literal, found expression `concat!("nonexistent")` - //~| ERROR expected unsuffixed literal, found expression `concat!("nonexistent")` struct S10; } } diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr index 21a3712d939..d02d0d70a8b 100644 --- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr @@ -65,19 +65,7 @@ LL | generate_s10!(concat!("nonexistent")); | = note: this error originates in the macro `generate_s10` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected unsuffixed literal, found expression `concat!("nonexistent")` - --> $DIR/cfg-attr-syntax-validation.rs:30:25 - | -LL | #[cfg(feature = $expr)] - | ^^^^^ -... -LL | generate_s10!(concat!("nonexistent")); - | ------------------------------------- in this macro invocation - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the macro `generate_s10` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors Some errors have detailed explanations: E0537, E0565. For more information about an error, try `rustc --explain E0537`. diff --git a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs index 794e6fad3fc..7c42be3ed4d 100644 --- a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs +++ b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs @@ -1,11 +1,12 @@ // This was triggering an assertion failure in `NodeRange::new`. +//@ check-pass + #![feature(cfg_eval)] #![feature(stmt_expr_attributes)] fn f() -> u32 { #[cfg_eval] #[cfg(not(FALSE))] 0 - //~^ ERROR removing an expression is not supported in this position } fn main() {} diff --git a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.stderr b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.stderr deleted file mode 100644 index 0699e182bd5..00000000000 --- a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: removing an expression is not supported in this position - --> $DIR/invalid-node-range-issue-129166.rs:7:17 - | -LL | #[cfg_eval] #[cfg(not(FALSE))] 0 - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/parser/attribute/attr-bad-meta-4.rs b/tests/ui/parser/attribute/attr-bad-meta-4.rs index 2d0c6dbb50a..937390a6da5 100644 --- a/tests/ui/parser/attribute/attr-bad-meta-4.rs +++ b/tests/ui/parser/attribute/attr-bad-meta-4.rs @@ -2,7 +2,6 @@ macro_rules! mac { ($attr_item: meta) => { #[cfg($attr_item)] //~^ ERROR expected unsuffixed literal, found `meta` metavariable - //~| ERROR expected unsuffixed literal, found `meta` metavariable struct S; } } @@ -11,7 +10,6 @@ mac!(an(arbitrary token stream)); #[cfg(feature = -1)] //~^ ERROR expected unsuffixed literal, found `-` -//~| ERROR expected unsuffixed literal, found `-` fn handler() {} fn main() {} diff --git a/tests/ui/parser/attribute/attr-bad-meta-4.stderr b/tests/ui/parser/attribute/attr-bad-meta-4.stderr index dea574fd36d..9c6ab5adadf 100644 --- a/tests/ui/parser/attribute/attr-bad-meta-4.stderr +++ b/tests/ui/parser/attribute/attr-bad-meta-4.stderr @@ -1,5 +1,5 @@ error: expected unsuffixed literal, found `-` - --> $DIR/attr-bad-meta-4.rs:12:17 + --> $DIR/attr-bad-meta-4.rs:11:17 | LL | #[cfg(feature = -1)] | ^ @@ -15,25 +15,5 @@ LL | mac!(an(arbitrary token stream)); | = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected unsuffixed literal, found `meta` metavariable - --> $DIR/attr-bad-meta-4.rs:3:15 - | -LL | #[cfg($attr_item)] - | ^^^^^^^^^^ -... -LL | mac!(an(arbitrary token stream)); - | -------------------------------- in this macro invocation - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: expected unsuffixed literal, found `-` - --> $DIR/attr-bad-meta-4.rs:12:17 - | -LL | #[cfg(feature = -1)] - | ^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/proc-macro/cfg-attr-trace.rs b/tests/ui/proc-macro/cfg-attr-trace.rs index b4927f7a730..140dd10a7e0 100644 --- a/tests/ui/proc-macro/cfg-attr-trace.rs +++ b/tests/ui/proc-macro/cfg-attr-trace.rs @@ -3,6 +3,7 @@ //@ check-pass //@ proc-macro: test-macros.rs +#![feature(cfg_boolean_literals)] #![feature(cfg_eval)] #[macro_use] @@ -10,8 +11,13 @@ extern crate test_macros; #[cfg_eval] #[test_macros::print_attr] -#[cfg_attr(FALSE, test_macros::print_attr)] -#[cfg_attr(all(), test_macros::print_attr)] +#[cfg_attr(false, test_macros::print_attr)] +#[cfg_attr(true, test_macros::print_attr)] struct S; +#[cfg_eval] +#[test_macros::print_attr] +#[cfg(true)] +struct Z; + fn main() {} diff --git a/tests/ui/proc-macro/cfg-attr-trace.stdout b/tests/ui/proc-macro/cfg-attr-trace.stdout index 394c3887fe7..52f9ff4e05c 100644 --- a/tests/ui/proc-macro/cfg-attr-trace.stdout +++ b/tests/ui/proc-macro/cfg-attr-trace.stdout @@ -4,59 +4,75 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: #0 bytes(271..272), + span: #0 bytes(305..306), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "test_macros", - span: #0 bytes(289..300), + span: #0 bytes(322..333), }, Punct { ch: ':', spacing: Joint, - span: #0 bytes(300..301), + span: #0 bytes(333..334), }, Punct { ch: ':', spacing: Alone, - span: #0 bytes(301..302), + span: #0 bytes(334..335), }, Ident { ident: "print_attr", - span: #0 bytes(302..312), + span: #0 bytes(335..345), }, ], - span: #0 bytes(272..314), + span: #0 bytes(306..347), }, Ident { ident: "struct", - span: #0 bytes(315..321), + span: #0 bytes(348..354), }, Ident { ident: "S", - span: #0 bytes(322..323), + span: #0 bytes(355..356), }, Punct { ch: ';', spacing: Alone, - span: #0 bytes(323..324), + span: #0 bytes(356..357), }, ] PRINT-ATTR INPUT (DISPLAY): struct S; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #0 bytes(315..321), + span: #0 bytes(348..354), }, Ident { ident: "S", - span: #0 bytes(322..323), + span: #0 bytes(355..356), }, Punct { ch: ';', spacing: Alone, - span: #0 bytes(323..324), + span: #0 bytes(356..357), + }, +] +PRINT-ATTR INPUT (DISPLAY): struct Z; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: #0 bytes(411..417), + }, + Ident { + ident: "Z", + span: #0 bytes(418..419), + }, + Punct { + ch: ';', + spacing: Alone, + span: #0 bytes(419..420), }, ] diff --git a/tests/ui/std/channel-stack-overflow-issue-102246.rs b/tests/ui/std/channel-stack-overflow-issue-102246.rs index 984ebdd553f..7bf6647bdc5 100644 --- a/tests/ui/std/channel-stack-overflow-issue-102246.rs +++ b/tests/ui/std/channel-stack-overflow-issue-102246.rs @@ -10,9 +10,16 @@ // Ref: https://github.com/rust-lang/rust/issues/102246 use std::sync::mpsc::channel; -use std::thread; +use std::thread::Builder; const N: usize = 32_768; +const SLOTS: usize = 32; +// Use a stack size that's smaller than N * SLOTS, proving the allocation is on the heap. +// +// The test explicitly specifies the stack size, because not all platforms have the same default +// size. +const STACK_SIZE: usize = (N*SLOTS) - 1; + struct BigStruct { _data: [u8; N], } @@ -20,10 +27,13 @@ struct BigStruct { fn main() { let (sender, receiver) = channel::<BigStruct>(); - let thread1 = thread::spawn(move || { + let thread1 = Builder::new().stack_size(STACK_SIZE).spawn(move || { sender.send(BigStruct { _data: [0u8; N] }).unwrap(); - }); - + }).expect("thread1 should spawn successfully"); thread1.join().unwrap(); - for _data in receiver.try_iter() {} + + let thread2 = Builder::new().stack_size(STACK_SIZE).spawn(move || { + for _data in receiver.try_iter() {} + }).expect("thread2 should spawn successfully"); + thread2.join().unwrap(); } diff --git a/tests/ui/structs/struct-construct-with-call-issue-138931.rs b/tests/ui/structs/struct-construct-with-call-issue-138931.rs new file mode 100644 index 00000000000..5d50eb14bff --- /dev/null +++ b/tests/ui/structs/struct-construct-with-call-issue-138931.rs @@ -0,0 +1,25 @@ +struct PersonOnlyName { + name: String +} + +struct PersonWithAge { + name: String, + age: u8, + height: u8, +} + + + +fn main() { + let wilfred = PersonOnlyName("Name1".to_owned()); + //~^ ERROR expected function, tuple struct or tuple variant, found struct `PersonOnlyName` [E0423] + + let bill = PersonWithAge( //~ ERROR expected function, tuple struct or tuple variant, found struct `PersonWithAge` [E0423] + "Name2".to_owned(), + 20, + 180, + ); + + let person = PersonWithAge("Name3".to_owned()); + //~^ ERROR expected function, tuple struct or tuple variant, found struct `PersonWithAge` [E0423] +} diff --git a/tests/ui/structs/struct-construct-with-call-issue-138931.stderr b/tests/ui/structs/struct-construct-with-call-issue-138931.stderr new file mode 100644 index 00000000000..acae01df563 --- /dev/null +++ b/tests/ui/structs/struct-construct-with-call-issue-138931.stderr @@ -0,0 +1,58 @@ +error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonOnlyName` + --> $DIR/struct-construct-with-call-issue-138931.rs:14:19 + | +LL | / struct PersonOnlyName { +LL | | name: String +LL | | } + | |_- `PersonOnlyName` defined here +... +LL | let wilfred = PersonOnlyName("Name1".to_owned()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use struct literal syntax instead of calling + | +LL - let wilfred = PersonOnlyName("Name1".to_owned()); +LL + let wilfred = PersonOnlyName{name: "Name1".to_owned()}; + | + +error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonWithAge` + --> $DIR/struct-construct-with-call-issue-138931.rs:17:16 + | +LL | / struct PersonWithAge { +LL | | name: String, +LL | | age: u8, +LL | | height: u8, +LL | | } + | |_- `PersonWithAge` defined here +... +LL | let bill = PersonWithAge( + | ________________^ +LL | | "Name2".to_owned(), +LL | | 20, +LL | | 180, +LL | | ); + | |_____^ + | +help: use struct literal syntax instead of calling + | +LL ~ let bill = PersonWithAge{name: "Name2".to_owned(), +LL ~ age: 20, +LL ~ height: 180}; + | + +error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonWithAge` + --> $DIR/struct-construct-with-call-issue-138931.rs:23:18 + | +LL | / struct PersonWithAge { +LL | | name: String, +LL | | age: u8, +LL | | height: u8, +LL | | } + | |_- `PersonWithAge` defined here +... +LL | let person = PersonWithAge("Name3".to_owned()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `PersonWithAge { name: val, age: val, height: val }` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/thread-local/spawn-hook-atexit.rs b/tests/ui/thread-local/spawn-hook-atexit.rs new file mode 100644 index 00000000000..b084e0bb387 --- /dev/null +++ b/tests/ui/thread-local/spawn-hook-atexit.rs @@ -0,0 +1,24 @@ +// Regression test for https://github.com/rust-lang/rust/issues/138696 +//@ only-unix +//@ needs-threads +//@ run-pass + +#![feature(rustc_private)] + +extern crate libc; + +fn main() { + std::thread::spawn(|| { + unsafe { libc::atexit(spawn_in_atexit) }; + }) + .join() + .unwrap(); +} + +extern "C" fn spawn_in_atexit() { + std::thread::spawn(|| { + println!("Thread spawned in atexit"); + }) + .join() + .unwrap(); +} |
