diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index ba819853ea06b..6357219750816 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -250,7 +250,7 @@ impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> { impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> { /// Get the current location within the Frame. /// - /// If this is `Left`, we are not currently executing any particular statement in + /// If this is `Right`, we are not currently executing any particular statement in /// this frame (can happen e.g. during frame initialization, and during unwinding on /// frames without cleanup code). /// @@ -500,6 +500,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + /// Returns the span of the currently executed statement/terminator. + /// This is the span typically used for error reporting. #[inline(always)] pub fn cur_span(&self) -> Span { // This deliberately does *not* honor `requires_caller_location` since it is used for much @@ -507,8 +509,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.stack().last().map_or(self.tcx.span, |f| f.current_span()) } + /// Find the first stack frame that is within the current crate, if any; + /// otherwise return the crate's HirId. #[inline(always)] - /// Find the first stack frame that is within the current crate, if any, otherwise return the crate's HirId pub fn best_lint_scope(&self) -> hir::HirId { self.stack() .iter() @@ -632,7 +635,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a - /// frame which is not `#[track_caller]`. This is the fancy version of `cur_span`. + /// frame which is not `#[track_caller]`. This matches the `caller_location` intrinsic, + /// and is primarily intended for the panic machinery. pub(crate) fn find_closest_untracked_caller_location(&self) -> Span { for frame in self.stack().iter().rev() { debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/stable_mir/src/crate_def.rs index 70ca9e6825e1f..67752a5e629f3 100644 --- a/compiler/stable_mir/src/crate_def.rs +++ b/compiler/stable_mir/src/crate_def.rs @@ -1,7 +1,7 @@ //! Module that define a common trait for things that represent a crate definition, //! such as, a function, a trait, an enum, and any other definitions. -use crate::ty::Span; +use crate::ty::{GenericArgs, Span, Ty}; use crate::{with, Crate, Symbol}; /// A unique identification number for each item accessible for the current compilation unit. @@ -52,6 +52,23 @@ pub trait CrateDef { } } +/// A trait that can be used to retrieve a definition's type. +/// +/// Note that not every CrateDef has a type `Ty`. They should not implement this trait. +pub trait CrateDefType: CrateDef { + /// Returns the type of this crate item. + fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.def_id())) + } + + /// Retrieve the type of this definition by instantiating and normalizing it with `args`. + /// + /// This will panic if instantiation fails. + fn ty_with_args(&self, args: &GenericArgs) -> Ty { + with(|cx| cx.def_ty_with_args(self.def_id(), args)) + } +} + macro_rules! crate_def { ( $(#[$attr:meta])* $vis:vis $name:ident $(;)? @@ -67,3 +84,21 @@ macro_rules! crate_def { } }; } + +macro_rules! crate_def_with_ty { + ( $(#[$attr:meta])* + $vis:vis $name:ident $(;)? + ) => { + $(#[$attr])* + #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] + $vis struct $name(pub DefId); + + impl CrateDef for $name { + fn def_id(&self) -> DefId { + self.0 + } + } + + impl CrateDefType for $name {} + }; +} diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index d9f988935ab19..8385856ae532d 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -22,8 +22,7 @@ use std::fmt::Debug; use std::io; use crate::compiler_interface::with; -pub use crate::crate_def::CrateDef; -pub use crate::crate_def::DefId; +pub use crate::crate_def::{CrateDef, CrateDefType, DefId}; pub use crate::error::*; use crate::mir::Body; use crate::mir::Mutability; @@ -115,12 +114,15 @@ pub enum CtorKind { pub type Filename = String; -crate_def! { +crate_def_with_ty! { /// Holds information about an item in a crate. pub CrateItem; } impl CrateItem { + /// This will return the body of an item. + /// + /// This will panic if no body is available. pub fn body(&self) -> mir::Body { with(|cx| cx.mir_body(self.0)) } diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 8c9824f8f8df8..76f94e6beba15 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -3,9 +3,10 @@ use super::{ with, DefId, Error, Symbol, }; use crate::abi::Layout; +use crate::crate_def::{CrateDef, CrateDefType}; use crate::mir::alloc::{read_target_int, read_target_uint, AllocId}; +use crate::mir::mono::StaticDef; use crate::target::MachineInfo; -use crate::{crate_def::CrateDef, mir::mono::StaticDef}; use crate::{Filename, Opaque}; use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Range; @@ -504,6 +505,15 @@ impl TyKind { pub fn discriminant_ty(&self) -> Option { self.rigid().map(|ty| with(|cx| cx.rigid_ty_discriminant_ty(ty))) } + + /// Deconstruct a function type if this is one. + pub fn fn_def(&self) -> Option<(FnDef, &GenericArgs)> { + if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = self { + Some((*def, args)) + } else { + None + } + } } pub struct TypeAndMut { @@ -629,7 +639,7 @@ impl ForeignModule { } } -crate_def! { +crate_def_with_ty! { /// Hold information about a ForeignItem in a crate. pub ForeignDef; } @@ -647,7 +657,7 @@ pub enum ForeignItemKind { Type(Ty), } -crate_def! { +crate_def_with_ty! { /// Hold information about a function definition in a crate. pub FnDef; } @@ -668,9 +678,15 @@ impl FnDef { pub fn is_intrinsic(&self) -> bool { self.as_intrinsic().is_some() } + + /// Get the function signature for this function definition. + pub fn fn_sig(&self) -> PolyFnSig { + let kind = self.ty().kind(); + kind.fn_sig().unwrap() + } } -crate_def! { +crate_def_with_ty! { pub IntrinsicDef; } @@ -710,7 +726,7 @@ crate_def! { pub BrNamedDef; } -crate_def! { +crate_def_with_ty! { pub AdtDef; } @@ -866,7 +882,7 @@ crate_def! { pub GenericDef; } -crate_def! { +crate_def_with_ty! { pub ConstDef; } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index cee99e28b5a97..733d414d44465 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -2080,8 +2080,7 @@ pub trait Iterator { fn try_collect(&mut self) -> ChangeOutputType where Self: Sized, - ::Item: Try, - <::Item as Try>::Residual: Residual, + Self::Item: Try>, B: FromIterator<::Output>, { try_process(ByRefSized(self), |i| i.collect()) @@ -2689,12 +2688,13 @@ pub trait Iterator { #[inline] #[unstable(feature = "iterator_try_reduce", reason = "new API", issue = "87053")] #[rustc_do_not_const_check] - fn try_reduce(&mut self, f: F) -> ChangeOutputType> + fn try_reduce( + &mut self, + f: impl FnMut(Self::Item, Self::Item) -> R, + ) -> ChangeOutputType> where Self: Sized, - F: FnMut(Self::Item, Self::Item) -> R, - R: Try, - R::Residual: Residual>, + R: Try>>, { let first = match self.next() { Some(i) => i, @@ -2956,12 +2956,13 @@ pub trait Iterator { #[inline] #[unstable(feature = "try_find", reason = "new API", issue = "63178")] #[rustc_do_not_const_check] - fn try_find(&mut self, f: F) -> ChangeOutputType> + fn try_find( + &mut self, + f: impl FnMut(&Self::Item) -> R, + ) -> ChangeOutputType> where Self: Sized, - F: FnMut(&Self::Item) -> R, - R: Try, - R::Residual: Residual>, + R: Try>>, { #[inline] fn check( diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 403fe4ff04c53..4d6ba2fa3b38c 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -672,7 +672,7 @@ impl Option { /// ``` #[must_use] #[inline] - #[unstable(feature = "is_none_or", issue = "none")] + #[unstable(feature = "is_none_or", issue = "126383")] pub fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool { match self { None => true, diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index da0196dad2ff7..cc972223f6dfb 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -14,6 +14,7 @@ use crate::common::{Config, Debugger, FailMode, Mode, PassMode}; use crate::header::cfg::parse_cfg_name_directive; use crate::header::cfg::MatchOutcome; use crate::header::needs::CachedNeedsConditions; +use crate::util::static_regex; use crate::{extract_cdb_version, extract_gdb_version}; mod cfg; @@ -1186,11 +1187,11 @@ impl Config { } } - fn parse_custom_normalization(&self, mut line: &str, prefix: &str) -> Option<(String, String)> { + fn parse_custom_normalization(&self, line: &str, prefix: &str) -> Option<(String, String)> { if parse_cfg_name_directive(self, line, prefix).outcome == MatchOutcome::Match { - let from = parse_normalization_string(&mut line)?; - let to = parse_normalization_string(&mut line)?; - Some((from, to)) + let (regex, replacement) = parse_normalize_rule(line) + .unwrap_or_else(|| panic!("couldn't parse custom normalization rule: `{line}`")); + Some((regex, replacement)) } else { None } @@ -1311,24 +1312,29 @@ fn expand_variables(mut value: String, config: &Config) -> String { value } -/// Finds the next quoted string `"..."` in `line`, and extract the content from it. Move the `line` -/// variable after the end of the quoted string. -/// -/// # Examples -/// -/// ``` -/// let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\"."; -/// let first = parse_normalization_string(&mut s); -/// assert_eq!(first, Some("something (32 bits)".to_owned())); -/// assert_eq!(s, " -> \"something ($WORD bits)\"."); +/// Parses the regex and replacement values of a `//@ normalize-*` header, +/// in the format: +/// ```text +/// normalize-*: "REGEX" -> "REPLACEMENT" /// ``` -fn parse_normalization_string(line: &mut &str) -> Option { - // FIXME support escapes in strings. - let begin = line.find('"')? + 1; - let end = line[begin..].find('"')? + begin; - let result = line[begin..end].to_owned(); - *line = &line[end + 1..]; - Some(result) +fn parse_normalize_rule(header: &str) -> Option<(String, String)> { + // FIXME(#126370): A colon after the header name should be mandatory, but + // currently is not, and there are many tests that lack the colon. + // FIXME: Support escaped double-quotes in strings. + let captures = static_regex!( + r#"(?x) # (verbose mode regex) + ^ + [^:\s]+:?\s* # (header name followed by optional colon) + "(?[^"]*)" # "REGEX" + \s+->\s+ # -> + "(?[^"]*)" # "REPLACEMENT" + $ + "# + ) + .captures(header)?; + let regex = captures["regex"].to_owned(); + let replacement = captures["replacement"].to_owned(); + Some((regex, replacement)) } pub fn extract_llvm_version(version: &str) -> Option { diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 8a37a4d6d3135..61a85b84ad64c 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -3,7 +3,7 @@ use std::path::Path; use std::str::FromStr; use crate::common::{Config, Debugger, Mode}; -use crate::header::{parse_normalization_string, EarlyProps, HeadersCache}; +use crate::header::{parse_normalize_rule, EarlyProps, HeadersCache}; use super::iter_header; @@ -32,35 +32,41 @@ fn make_test_description( } #[test] -fn test_parse_normalization_string() { - let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\"."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, Some("something (32 bits)".to_owned())); - assert_eq!(s, " -> \"something ($WORD bits)\"."); - - // Nothing to normalize (No quotes) - let mut s = "normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, None); - assert_eq!(s, r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."#); - - // Nothing to normalize (Only a single quote) - let mut s = "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, None); - assert_eq!(s, "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits)."); - - // Nothing to normalize (Three quotes) - let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, Some("something (32 bits)".to_owned())); - assert_eq!(s, " -> \"something ($WORD bits)."); - - // Nothing to normalize (No quotes, 16-bit) - let mut s = "normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, None); - assert_eq!(s, r#"normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)."#); +fn test_parse_normalize_rule() { + let good_data = &[ + ( + r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)""#, + "something (32 bits)", + "something ($WORD bits)", + ), + // FIXME(#126370): A colon after the header name should be mandatory, + // but currently is not, and there are many tests that lack the colon. + ( + r#"normalize-stderr-32bit "something (32 bits)" -> "something ($WORD bits)""#, + "something (32 bits)", + "something ($WORD bits)", + ), + ]; + + for &(input, expected_regex, expected_replacement) in good_data { + let parsed = parse_normalize_rule(input); + let parsed = + parsed.as_ref().map(|(regex, replacement)| (regex.as_str(), replacement.as_str())); + assert_eq!(parsed, Some((expected_regex, expected_replacement))); + } + + let bad_data = &[ + r#"normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)"#, + r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)"#, + r#"normalize-stderr-32bit: "something (32 bits) -> something ($WORD bits)"#, + r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)"#, + r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)"."#, + ]; + + for &input in bad_data { + let parsed = parse_normalize_rule(input); + assert_eq!(parsed, None); + } } #[derive(Default)] diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index cc15961b46ed8..9e1d5ea61aa8b 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -15,7 +15,7 @@ use crate::errors::{self, Error, ErrorKind}; use crate::header::TestProps; use crate::json; use crate::read2::{read2_abbreviated, Truncated}; -use crate::util::{add_dylib_path, copy_dir_all, dylib_env_var, logv, PathBufExt}; +use crate::util::{add_dylib_path, copy_dir_all, dylib_env_var, logv, static_regex, PathBufExt}; use crate::ColorConfig; use colored::Colorize; use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile}; @@ -48,14 +48,6 @@ use debugger::DebuggerCommands; #[cfg(test)] mod tests; -macro_rules! static_regex { - ($re:literal) => {{ - static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new(); - RE.get_or_init(|| ::regex::Regex::new($re).unwrap()) - }}; -} -use static_regex; - const FAKE_SRC_BASE: &str = "fake-test-src-base"; #[cfg(windows)] diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs index 8bd7c7e808d3f..6ee147da5a965 100644 --- a/src/tools/compiletest/src/runtest/coverage.rs +++ b/src/tools/compiletest/src/runtest/coverage.rs @@ -7,7 +7,8 @@ use std::process::Command; use glob::glob; use crate::common::{UI_COVERAGE, UI_COVERAGE_MAP}; -use crate::runtest::{static_regex, Emit, ProcRes, TestCx, WillExecute}; +use crate::runtest::{Emit, ProcRes, TestCx, WillExecute}; +use crate::util::static_regex; impl<'test> TestCx<'test> { fn coverage_dump_path(&self) -> &Path { diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index ec20bda8c1896..cdec49a51d75b 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -90,3 +90,11 @@ pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> std::io::Re } Ok(()) } + +macro_rules! static_regex { + ($re:literal) => {{ + static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new(); + RE.get_or_init(|| ::regex::Regex::new($re).unwrap()) + }}; +} +pub(crate) use static_regex; diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs index 2c97a39f1008b..b54fa5c6b2c92 100644 --- a/src/tools/tidy/src/ext_tool_checks.rs +++ b/src/tools/tidy/src/ext_tool_checks.rs @@ -274,13 +274,18 @@ fn create_venv_at_path(path: &Path) -> Result<(), Error> { if out.status.success() { return Ok(()); } - let err = if String::from_utf8_lossy(&out.stderr).contains("No module named virtualenv") { + + let stderr = String::from_utf8_lossy(&out.stderr); + let err = if stderr.contains("No module named virtualenv") { Error::Generic(format!( "virtualenv not found: you may need to install it \ (`python3 -m pip install venv`)" )) } else { - Error::Generic(format!("failed to create venv at '{}' using {sys_py}", path.display())) + Error::Generic(format!( + "failed to create venv at '{}' using {sys_py}: {stderr}", + path.display() + )) }; Err(err) } diff --git a/tests/ui-fulldeps/stable-mir/check_def_ty.rs b/tests/ui-fulldeps/stable-mir/check_def_ty.rs new file mode 100644 index 0000000000000..9f45b62d34345 --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_def_ty.rs @@ -0,0 +1,114 @@ +//@ run-pass +//! Test that users are able to use stable mir APIs to retrieve type information from a crate item +//! definition. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 +//@ edition: 2021 + +#![feature(rustc_private)] +#![feature(assert_matches)] +#![feature(control_flow_enum)] + +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_smir::rustc_internal; +use stable_mir::ty::{Ty, ForeignItemKind}; +use stable_mir::*; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "crate_def_ty"; + +/// Test if we can retrieve type information from different definitions. +fn test_def_tys() -> ControlFlow<()> { + let items = stable_mir::all_local_items(); + for item in &items { + // Type from crate items. + let ty = item.ty(); + match item.name().as_str() { + "STATIC_STR" => assert!(ty.kind().is_ref()), + "CONST_U32" => assert!(ty.kind().is_integral()), + "main" => { check_fn_def(ty) } + _ => unreachable!("Unexpected item: `{item:?}`") + } + } + + let foreign_items = stable_mir::local_crate().foreign_modules(); + for item in foreign_items[0].module().items() { + // Type from foreign items. + let ty = item.ty(); + let item_kind = item.kind(); + let name = item.name(); + match item_kind { + ForeignItemKind::Fn(fn_def) => { + assert_eq!(&name, "extern_fn"); + assert_eq!(ty, fn_def.ty()); + check_fn_def(ty) + } + ForeignItemKind::Static(def) => { + assert_eq!(&name, "EXT_STATIC"); + assert_eq!(ty, def.ty()); + assert!(ty.kind().is_integral()) + } + _ => unreachable!("Unexpected kind: {item_kind:?}") + }; + } + + ControlFlow::Continue(()) +} + +fn check_fn_def(ty: Ty) { + let kind = ty.kind(); + let (def, args) = kind.fn_def().expect(&format!("Expected function type, but found: {ty}")); + assert!(def.ty().kind().is_fn()); + assert_eq!(def.ty_with_args(args), ty); +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "defs_ty_input.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "-Cpanic=abort".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, test_def_tys).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + // We would like to check intrinsic definition. + #![feature(core_intrinsics)] + static STATIC_STR: &str = "foo"; + const CONST_U32: u32 = 0u32; + + fn main() {{ + let _c = core::char::from_u32(99); + let _v = Vec::::new(); + let _i = std::intrinsics::size_of::(); + }} + + extern "C" {{ + fn extern_fn(x: i32) -> i32; + static EXT_STATIC: i32; + }} + "# + )?; + Ok(()) +}