diff --git a/Cargo.lock b/Cargo.lock index 17856e1f52a35..b1bdaef81ff83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3670,6 +3670,7 @@ dependencies = [ "rustc_metadata", "rustc_middle", "rustc_query_system", + "rustc_sanitizers", "rustc_session", "rustc_span", "rustc_symbol_mangling", @@ -4558,6 +4559,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "rustc_sanitizers" +version = "0.0.0" +dependencies = [ + "bitflags 2.5.0", + "rustc_data_structures", + "rustc_hir", + "rustc_middle", + "rustc_span", + "rustc_target", + "rustc_trait_selection", + "tracing", + "twox-hash", +] + [[package]] name = "rustc_serialize" version = "0.0.0" @@ -4633,7 +4649,6 @@ dependencies = [ name = "rustc_symbol_mangling" version = "0.0.0" dependencies = [ - "bitflags 2.5.0", "punycode", "rustc-demangle", "rustc_data_structures", @@ -4643,9 +4658,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "rustc_trait_selection", "tracing", - "twox-hash", ] [[package]] diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 3fda59e8b5234..bb5045ec87241 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -28,6 +28,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } rustc_query_system = { path = "../rustc_query_system" } +rustc_sanitizers = { path = "../rustc_sanitizers" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 1a32958d3627b..b7235972204d7 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -20,12 +20,9 @@ use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout, }; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_sanitizers::{cfi, kcfi}; use rustc_session::config::OptLevel; use rustc_span::Span; -use rustc_symbol_mangling::typeid::{ - kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, - TypeIdOptions, -}; use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange}; use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target}; use smallvec::SmallVec; @@ -1632,18 +1629,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return; } - let mut options = TypeIdOptions::empty(); + let mut options = cfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(cfi::TypeIdOptions::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(cfi::TypeIdOptions::NORMALIZE_INTEGERS); } let typeid = if let Some(instance) = instance { - typeid_for_instance(self.tcx, instance, options) + cfi::typeid_for_instance(self.tcx, instance, options) } else { - typeid_for_fnabi(self.tcx, fn_abi, options) + cfi::typeid_for_fnabi(self.tcx, fn_abi, options) }; let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap(); @@ -1680,18 +1677,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return None; } - let mut options = TypeIdOptions::empty(); + let mut options = kcfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } let kcfi_typeid = if let Some(instance) = instance { - kcfi_typeid_for_instance(self.tcx, instance, options) + kcfi::typeid_for_instance(self.tcx, instance, options) } else { - kcfi_typeid_for_fnabi(self.tcx, fn_abi, options) + kcfi::typeid_for_fnabi(self.tcx, fn_abi, options) }; Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)])) diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index f86cdcaa6f796..7117c4a0ed910 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -22,10 +22,7 @@ use itertools::Itertools; use rustc_codegen_ssa::traits::TypeMembershipMethods; use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{Instance, Ty}; -use rustc_symbol_mangling::typeid::{ - kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, - TypeIdOptions, -}; +use rustc_sanitizers::{cfi, kcfi}; use smallvec::SmallVec; /// Declare a function. @@ -145,27 +142,29 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { if let Some(instance) = instance { let mut typeids = FxIndexSet::default(); for options in [ - TypeIdOptions::GENERALIZE_POINTERS, - TypeIdOptions::NORMALIZE_INTEGERS, - TypeIdOptions::USE_CONCRETE_SELF, + cfi::TypeIdOptions::GENERALIZE_POINTERS, + cfi::TypeIdOptions::NORMALIZE_INTEGERS, + cfi::TypeIdOptions::USE_CONCRETE_SELF, ] .into_iter() .powerset() - .map(TypeIdOptions::from_iter) + .map(cfi::TypeIdOptions::from_iter) { - let typeid = typeid_for_instance(self.tcx, instance, options); + let typeid = cfi::typeid_for_instance(self.tcx, instance, options); if typeids.insert(typeid.clone()) { self.add_type_metadata(llfn, typeid); } } } else { - for options in - [TypeIdOptions::GENERALIZE_POINTERS, TypeIdOptions::NORMALIZE_INTEGERS] - .into_iter() - .powerset() - .map(TypeIdOptions::from_iter) + for options in [ + cfi::TypeIdOptions::GENERALIZE_POINTERS, + cfi::TypeIdOptions::NORMALIZE_INTEGERS, + ] + .into_iter() + .powerset() + .map(cfi::TypeIdOptions::from_iter) { - let typeid = typeid_for_fnabi(self.tcx, fn_abi, options); + let typeid = cfi::typeid_for_fnabi(self.tcx, fn_abi, options); self.add_type_metadata(llfn, typeid); } } @@ -173,19 +172,19 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { if self.tcx.sess.is_sanitizer_kcfi_enabled() { // LLVM KCFI does not support multiple !kcfi_type attachments - let mut options = TypeIdOptions::empty(); + let mut options = kcfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } if let Some(instance) = instance { - let kcfi_typeid = kcfi_typeid_for_instance(self.tcx, instance, options); + let kcfi_typeid = kcfi::typeid_for_instance(self.tcx, instance, options); self.set_kcfi_type_metadata(llfn, kcfi_typeid); } else { - let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); + let kcfi_typeid = kcfi::typeid_for_fnabi(self.tcx, fn_abi, options); self.set_kcfi_type_metadata(llfn, kcfi_typeid); } } diff --git a/compiler/rustc_error_codes/src/error_codes/E0699.md b/compiler/rustc_error_codes/src/error_codes/E0699.md index 454d2507e5e2e..1094ebf4b8fc7 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0699.md +++ b/compiler/rustc_error_codes/src/error_codes/E0699.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + A method was called on a raw pointer whose inner type wasn't completely known. Erroneous code example: -```compile_fail,edition2018,E0699 +```compile_fail,edition2018 # #![deny(warnings)] # fn main() { let foo = &1; diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index da688e385aa09..f4a33a05c1b36 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -441,7 +441,7 @@ E0695: 0695, E0696: 0696, E0697: 0697, E0698: 0698, -E0699: 0699, +E0699: 0699, // REMOVED: merged into generic inference var error E0700: 0700, E0701: 0701, E0703: 0703, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 129ce1d3109f4..e6b19817de385 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -216,7 +216,7 @@ declare_features! ( /// Set the maximum pattern complexity allowed (not limited by default). (internal, pattern_complexity, "1.78.0", None), /// Allows using pattern types. - (internal, pattern_types, "CURRENT_RUSTC_VERSION", Some(54882)), + (internal, pattern_types, "CURRENT_RUSTC_VERSION", Some(123646)), /// Allows using `#[prelude_import]` on glob `use` items. (internal, prelude_import, "1.2.0", None), /// Used to identify crates that contain the profiler runtime. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index ebfccd27d1730..59f0fac5aa76f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2249,7 +2249,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_pat(tcx, ty, pat) } hir::PatKind::Err(e) => Ty::new_error(tcx, e), - _ => span_bug!(pat.span, "unsupported pattern for pattern type: {pat:#?}"), + _ => Ty::new_error_with_message( + tcx, + pat.span, + format!("unsupported pattern for pattern type: {pat:#?}"), + ), }; self.record_ty(pat.hir_id, ty, pat.span); pat_ty diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 1d51101c94031..18d9d739dd696 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -93,9 +93,6 @@ hir_typeck_lossy_provenance_ptr2int = .suggestion = use `.addr()` to obtain the address of a pointer .help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead -hir_typeck_method_call_on_unknown_raw_pointee = - cannot call a method on a raw pointer with an unknown pointee type - hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}` hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method -> diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index f9b2ec697300b..d399730bf3df4 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -76,13 +76,6 @@ pub struct StructExprNonExhaustive { pub what: &'static str, } -#[derive(Diagnostic)] -#[diag(hir_typeck_method_call_on_unknown_raw_pointee, code = E0699)] -pub struct MethodCallOnUnknownRawPointee { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(hir_typeck_functional_record_update_on_non_struct, code = E0436)] pub struct FunctionalRecordUpdateOnNonStruct { diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 4e63600dbdffa..28e17e1de36c3 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,7 +3,6 @@ use super::CandidateSource; use super::MethodError; use super::NoMatchData; -use crate::errors::MethodCallOnUnknownRawPointee; use crate::FnCtxt; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -430,21 +429,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if is_suggestion.0 { // Ambiguity was encountered during a suggestion. Just keep going. debug!("ProbeContext: encountered ambiguity in suggestion"); - } else if bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types { + } else if bad_ty.reached_raw_pointer + && !self.tcx.features().arbitrary_self_types + && !self.tcx.sess.at_least_rust_2018() + { // this case used to be allowed by the compiler, // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) - if self.tcx.sess.at_least_rust_2018() { - self.dcx().emit_err(MethodCallOnUnknownRawPointee { span }); - } else { - self.tcx.node_span_lint( - lint::builtin::TYVAR_BEHIND_RAW_POINTER, - scope_expr_id, - span, - "type annotations needed", - |_| {}, - ); - } + self.tcx.node_span_lint( + lint::builtin::TYVAR_BEHIND_RAW_POINTER, + scope_expr_id, + span, + "type annotations needed", + |_| {}, + ); } else { // Ended up encountering a type variable when doing autoderef, // but it may not be a type variable after processing obligations @@ -455,10 +453,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); let ty = self.resolve_vars_if_possible(ty.value); let guar = match *ty.kind() { - ty::Infer(ty::TyVar(_)) => self - .err_ctxt() - .emit_inference_failure_err(self.body_id, span, ty.into(), E0282, true) - .emit(), + ty::Infer(ty::TyVar(_)) => { + let raw_ptr_call = + bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types; + let mut err = self.err_ctxt().emit_inference_failure_err( + self.body_id, + span, + ty.into(), + E0282, + !raw_ptr_call, + ); + if raw_ptr_call { + err.span_label(span, "cannot call a method on a raw pointer with an unknown pointee type"); + } + err.emit() + } ty::Error(guar) => guar, _ => bug!("unexpected bad final type in method autoderef"), }; diff --git a/compiler/rustc_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml new file mode 100644 index 0000000000000..aea2f7dda7f36 --- /dev/null +++ b/compiler/rustc_sanitizers/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rustc_sanitizers" +version = "0.0.0" +edition = "2021" + +[dependencies] +bitflags = "2.5.0" +tracing = "0.1" +twox-hash = "1.6.3" +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_hir = { path = "../rustc_hir" } +rustc_middle = { path = "../rustc_middle" } +rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } +rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/compiler/rustc_sanitizers/README.md b/compiler/rustc_sanitizers/README.md new file mode 100644 index 0000000000000..d2e8f5d3a97b5 --- /dev/null +++ b/compiler/rustc_sanitizers/README.md @@ -0,0 +1,2 @@ +The `rustc_sanitizers` crate contains the source code for providing support for +the [sanitizers](https://github.com/google/sanitizers) to the Rust compiler. diff --git a/compiler/rustc_sanitizers/src/cfi/mod.rs b/compiler/rustc_sanitizers/src/cfi/mod.rs new file mode 100644 index 0000000000000..90dab5e0333a1 --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/mod.rs @@ -0,0 +1,6 @@ +//! LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI support for the Rust compiler. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +pub mod typeid; +pub use crate::cfi::typeid::{typeid_for_fnabi, typeid_for_instance, TypeIdOptions}; diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs similarity index 54% rename from compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs rename to compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 6078d90171194..ed7cd8c2da745 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -1,76 +1,46 @@ -/// Type metadata identifiers (using Itanium C++ ABI mangling for encoding) for LLVM Control Flow -/// Integrity (CFI) and cross-language LLVM CFI support. -/// -/// Encodes type metadata identifiers for LLVM CFI and cross-language LLVM CFI support using Itanium -/// C++ ABI mangling for encoding with vendor extended type qualifiers and types for Rust types that -/// are not used across the FFI boundary. -/// -/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, -/// see design document in the tracking issue #89653. +//! Encodes type metadata identifiers for LLVM CFI and cross-language LLVM CFI support using Itanium +//! C++ ABI mangling for encoding with vendor extended type qualifiers and types for Rust types that +//! are not used across the FFI boundary. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. use rustc_data_structures::base_n; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::lang_items::LangItem; -use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; +use rustc_middle::bug; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind, - TermKind, Ty, TyCtxt, UintTy, + self, Const, ExistentialPredicate, FloatTy, FnSig, GenericArg, GenericArgKind, GenericArgsRef, + IntTy, List, Region, RegionKind, TermKind, Ty, TyCtxt, TypeFoldable, UintTy, }; -use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; -use rustc_middle::ty::{TypeFoldable, TypeVisitableExt}; use rustc_span::def_id::DefId; use rustc_span::sym; -use rustc_target::abi::call::{Conv, FnAbi, PassMode}; use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits; use std::fmt::Write as _; -use std::iter; +use tracing::instrument; -use crate::typeid::TypeIdOptions; +use crate::cfi::typeid::itanium_cxx_abi::transform::{TransformTy, TransformTyOptions}; +use crate::cfi::typeid::TypeIdOptions; -/// Type and extended type qualifiers. -#[derive(Eq, Hash, PartialEq)] -enum TyQ { - None, - Const, - Mut, -} +/// Options for encode_ty. +pub type EncodeTyOptions = TypeIdOptions; /// Substitution dictionary key. #[derive(Eq, Hash, PartialEq)] -enum DictKey<'tcx> { +pub enum DictKey<'tcx> { Ty(Ty<'tcx>, TyQ), Region(Region<'tcx>), Const(Const<'tcx>), Predicate(ExistentialPredicate<'tcx>), } -/// Options for encode_ty. -type EncodeTyOptions = TypeIdOptions; - -/// Options for transform_ty. -type TransformTyOptions = TypeIdOptions; - -/// Converts a number to a disambiguator (see -/// ). -fn to_disambiguator(num: u64) -> String { - if let Some(num) = num.checked_sub(1) { - format!("s{}_", base_n::encode(num as u128, 62)) - } else { - "s_".to_string() - } -} - -/// Converts a number to a sequence number (see -/// ). -fn to_seq_id(num: usize) -> String { - if let Some(num) = num.checked_sub(1) { - base_n::encode(num as u128, 36).to_uppercase() - } else { - "".to_string() - } +/// Type and extended type qualifiers. +#[derive(Eq, Hash, PartialEq)] +pub enum TyQ { + None, + Const, + Mut, } /// Substitutes a component if found in the substitution dictionary (see @@ -91,6 +61,37 @@ fn compress<'tcx>( } } +/// Encodes args using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust +/// types that are not used at the FFI boundary. +fn encode_args<'tcx>( + tcx: TyCtxt<'tcx>, + args: GenericArgsRef<'tcx>, + dict: &mut FxHashMap, usize>, + options: EncodeTyOptions, +) -> String { + // [IE] as part of vendor extended type + let mut s = String::new(); + let args: Vec> = args.iter().collect(); + if !args.is_empty() { + s.push('I'); + for arg in args { + match arg.unpack() { + GenericArgKind::Lifetime(region) => { + s.push_str(&encode_region(region, dict)); + } + GenericArgKind::Type(ty) => { + s.push_str(&encode_ty(tcx, ty, dict, options)); + } + GenericArgKind::Const(c) => { + s.push_str(&encode_const(tcx, c, dict, options)); + } + } + } + s.push('E'); + } + s +} + /// Encodes a const using the Itanium C++ ABI as a literal argument (see /// ). fn encode_const<'tcx>( @@ -159,7 +160,6 @@ fn encode_const<'tcx>( /// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for /// Rust types that are not used at the FFI boundary. -#[instrument(level = "trace", skip(tcx, dict))] fn encode_fnsig<'tcx>( tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>, @@ -299,137 +299,10 @@ fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap, s } -/// Encodes args using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust -/// types that are not used at the FFI boundary. -fn encode_args<'tcx>( - tcx: TyCtxt<'tcx>, - args: GenericArgsRef<'tcx>, - dict: &mut FxHashMap, usize>, - options: EncodeTyOptions, -) -> String { - // [IE] as part of vendor extended type - let mut s = String::new(); - let args: Vec> = args.iter().collect(); - if !args.is_empty() { - s.push('I'); - for arg in args { - match arg.unpack() { - GenericArgKind::Lifetime(region) => { - s.push_str(&encode_region(region, dict)); - } - GenericArgKind::Type(ty) => { - s.push_str(&encode_ty(tcx, ty, dict, options)); - } - GenericArgKind::Const(c) => { - s.push_str(&encode_const(tcx, c, dict, options)); - } - } - } - s.push('E'); - } - s -} - -/// Encodes a ty:Ty name, including its crate and path disambiguators and names. -fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String { - // Encode for use in u[IE], where - // is , using v0's without v0's extended form of paths: - // - // N..N - // C - // .. - // - // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance: - // - // pub type Type1 = impl Send; - // let _: Type1 = >::foo; - // fn foo1(_: Type1) { } - // - // pub type Type2 = impl Send; - // let _: Type2 = >::foo; - // fn foo2(_: Type2) { } - // - // pub type Type3 = impl Send; - // let _: Type3 = >::foo; - // fn foo3(_: Type3) { } - // - // pub type Type4 = impl Send; - // let _: Type4 = as Trait1>::foo; - // fn foo3(_: Type4) { } - // - // Are encoded as: - // - // _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE - // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE - // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE - // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE - // - // The reason for not using v0's extended form of paths is to use a consistent and simpler - // encoding, as the reasoning for using it isn't relevant for type metadata identifiers (i.e., - // keep symbol names close to how methods are represented in error messages). See - // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods. - let mut s = String::new(); - - // Start and namespace tags - let mut def_path = tcx.def_path(def_id); - def_path.data.reverse(); - for disambiguated_data in &def_path.data { - s.push('N'); - s.push_str(match disambiguated_data.data { - hir::definitions::DefPathData::Impl => "I", // Not specified in v0's - hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's - hir::definitions::DefPathData::TypeNs(..) => "t", - hir::definitions::DefPathData::ValueNs(..) => "v", - hir::definitions::DefPathData::Closure => "C", - hir::definitions::DefPathData::Ctor => "c", - hir::definitions::DefPathData::AnonConst => "k", - hir::definitions::DefPathData::OpaqueTy => "i", - hir::definitions::DefPathData::CrateRoot - | hir::definitions::DefPathData::Use - | hir::definitions::DefPathData::GlobalAsm - | hir::definitions::DefPathData::MacroNs(..) - | hir::definitions::DefPathData::LifetimeNs(..) - | hir::definitions::DefPathData::AnonAdt => { - bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data); - } - }); - } - - // Crate disambiguator and name - s.push('C'); - s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64())); - let crate_name = tcx.crate_name(def_path.krate).to_string(); - let _ = write!(s, "{}{}", crate_name.len(), &crate_name); - - // Disambiguators and names - def_path.data.reverse(); - for disambiguated_data in &def_path.data { - let num = disambiguated_data.disambiguator as u64; - if num > 0 { - s.push_str(&to_disambiguator(num)); - } - - let name = disambiguated_data.data.to_string(); - let _ = write!(s, "{}", name.len()); - - // Prepend a '_' if name starts with a digit or '_' - if let Some(first) = name.as_bytes().first() { - if first.is_ascii_digit() || *first == b'_' { - s.push('_'); - } - } else { - bug!("encode_ty_name: invalid name `{:?}`", name); - } - - s.push_str(&name); - } - - s -} - /// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for /// Rust types that are not used at the FFI boundary. -fn encode_ty<'tcx>( +#[instrument(level = "trace", skip(tcx, dict))] +pub fn encode_ty<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, dict: &mut FxHashMap, usize>, @@ -762,486 +635,119 @@ fn encode_ty<'tcx>( typeid } -struct TransformTy<'tcx> { - tcx: TyCtxt<'tcx>, - options: TransformTyOptions, - parents: Vec>, -} - -impl<'tcx> TransformTy<'tcx> { - fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self { - TransformTy { tcx, options, parents: Vec::new() } - } -} - -impl<'tcx> TypeFolder> for TransformTy<'tcx> { - // Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms - // all c_void types into unit types unconditionally, generalizes pointers if - // TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if - // TransformTyOptions::NORMALIZE_INTEGERS option is set. - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind() { - ty::Array(..) - | ty::Closure(..) - | ty::Coroutine(..) - | ty::CoroutineClosure(..) - | ty::CoroutineWitness(..) - | ty::Dynamic(..) - | ty::Float(..) - | ty::FnDef(..) - | ty::Foreign(..) - | ty::Never - | ty::Slice(..) - | ty::Pat(..) - | ty::Str - | ty::Tuple(..) => t.super_fold_with(self), - - ty::Bool => { - if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Note: on all platforms that Rust's currently supports, its size and alignment - // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs. - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) - // - // Clang represents bool as an 8-bit unsigned integer. - self.tcx.types.u8 - } else { - t - } - } - - ty::Char => { - if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Since #118032, char is guaranteed to have the same size, alignment, and - // function call ABI as u32 on all platforms. - self.tcx.types.u32 - } else { - t - } - } - - ty::Int(..) | ty::Uint(..) => { - if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit - // wide. All platforms we currently support have a C platform, and as a - // consequence, isize/usize are at least 16-bit wide for all of them. - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.) - match t.kind() { - ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width { - 16 => self.tcx.types.i16, - 32 => self.tcx.types.i32, - 64 => self.tcx.types.i64, - 128 => self.tcx.types.i128, - _ => bug!( - "fold_ty: unexpected pointer width `{}`", - self.tcx.sess.target.pointer_width - ), - }, - ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width { - 16 => self.tcx.types.u16, - 32 => self.tcx.types.u32, - 64 => self.tcx.types.u64, - 128 => self.tcx.types.u128, - _ => bug!( - "fold_ty: unexpected pointer width `{}`", - self.tcx.sess.target.pointer_width - ), - }, - _ => t, - } - } else { - t - } - } - - ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit, - - ty::Adt(adt_def, args) => { - if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t) - { - // Don't transform repr(transparent) types with an user-defined CFI encoding to - // preserve the user-defined CFI encoding. - if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) { - return t; - } - let variant = adt_def.non_enum_variant(); - let param_env = self.tcx.param_env(variant.def_id); - let field = variant.fields.iter().find(|field| { - let ty = self.tcx.type_of(field.did).instantiate_identity(); - let is_zst = self - .tcx - .layout_of(param_env.and(ty)) - .is_ok_and(|layout| layout.is_zst()); - !is_zst - }); - if let Some(field) = field { - let ty0 = self.tcx.type_of(field.did).instantiate(self.tcx, args); - // Generalize any repr(transparent) user-defined type that is either a - // pointer or reference, and either references itself or any other type that - // contains or references itself, to avoid a reference cycle. - - // If the self reference is not through a pointer, for example, due - // to using `PhantomData`, need to skip normalizing it if we hit it again. - self.parents.push(t); - let ty = if ty0.is_any_ptr() && ty0.contains(t) { - let options = self.options; - self.options |= TransformTyOptions::GENERALIZE_POINTERS; - let ty = ty0.fold_with(self); - self.options = options; - ty - } else { - ty0.fold_with(self) - }; - self.parents.pop(); - ty - } else { - // Transform repr(transparent) types without non-ZST field into () - self.tcx.types.unit - } - } else { - t.super_fold_with(self) - } - } - - ty::Ref(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if t.is_mutable_ptr() { - Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) - } else { - Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) - } - } else { - t.super_fold_with(self) - } - } - - ty::RawPtr(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if t.is_mutable_ptr() { - Ty::new_mut_ptr(self.tcx, self.tcx.types.unit) - } else { - Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) - } - } else { - t.super_fold_with(self) - } - } - - ty::FnPtr(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) - } else { - t.super_fold_with(self) - } - } - - ty::Alias(..) => { - self.fold_ty(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t)) - } +/// Encodes a ty:Ty name, including its crate and path disambiguators and names. +fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String { + // Encode for use in u[IE], where + // is , using v0's without v0's extended form of paths: + // + // N..N + // C + // .. + // + // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance: + // + // pub type Type1 = impl Send; + // let _: Type1 = >::foo; + // fn foo1(_: Type1) { } + // + // pub type Type2 = impl Send; + // let _: Type2 = >::foo; + // fn foo2(_: Type2) { } + // + // pub type Type3 = impl Send; + // let _: Type3 = >::foo; + // fn foo3(_: Type3) { } + // + // pub type Type4 = impl Send; + // let _: Type4 = as Trait1>::foo; + // fn foo3(_: Type4) { } + // + // Are encoded as: + // + // _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE + // + // The reason for not using v0's extended form of paths is to use a consistent and simpler + // encoding, as the reasoning for using it isn't relevant for type metadata identifiers (i.e., + // keep symbol names close to how methods are represented in error messages). See + // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods. + let mut s = String::new(); - ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { - bug!("fold_ty: unexpected `{:?}`", t.kind()); + // Start and namespace tags + let mut def_path = tcx.def_path(def_id); + def_path.data.reverse(); + for disambiguated_data in &def_path.data { + s.push('N'); + s.push_str(match disambiguated_data.data { + hir::definitions::DefPathData::Impl => "I", // Not specified in v0's + hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's + hir::definitions::DefPathData::TypeNs(..) => "t", + hir::definitions::DefPathData::ValueNs(..) => "v", + hir::definitions::DefPathData::Closure => "C", + hir::definitions::DefPathData::Ctor => "c", + hir::definitions::DefPathData::AnonConst => "k", + hir::definitions::DefPathData::OpaqueTy => "i", + hir::definitions::DefPathData::CrateRoot + | hir::definitions::DefPathData::Use + | hir::definitions::DefPathData::GlobalAsm + | hir::definitions::DefPathData::MacroNs(..) + | hir::definitions::DefPathData::LifetimeNs(..) + | hir::definitions::DefPathData::AnonAdt => { + bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data); } - } - } - - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx + }); } -} - -/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor -/// extended type qualifiers and types for Rust types that are not used at the FFI boundary. -#[instrument(level = "trace", skip(tcx))] -pub fn typeid_for_fnabi<'tcx>( - tcx: TyCtxt<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, -) -> String { - // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions - // its type. - let mut typeid = String::from("_Z"); - // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type - // metadata identifiers for function pointers. The typeinfo name encoding is a two-character - // code (i.e., 'TS') prefixed to the type encoding for the function. - typeid.push_str("TS"); - - // Function types are delimited by an "F..E" pair - typeid.push('F'); - - // A dictionary of substitution candidates used for compression (see - // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). - let mut dict: FxHashMap, usize> = FxHashMap::default(); + // Crate disambiguator and name + s.push('C'); + s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64())); + let crate_name = tcx.crate_name(def_path.krate).to_string(); + let _ = write!(s, "{}{}", crate_name.len(), &crate_name); - let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); - match fn_abi.conv { - Conv::C => { - encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); - } - _ => { - encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); + // Disambiguators and names + def_path.data.reverse(); + for disambiguated_data in &def_path.data { + let num = disambiguated_data.disambiguator as u64; + if num > 0 { + s.push_str(&to_disambiguator(num)); } - } - - // Encode the return type - let transform_ty_options = TransformTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); - let mut type_folder = TransformTy::new(tcx, transform_ty_options); - let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); - // Encode the parameter types + let name = disambiguated_data.data.to_string(); + let _ = write!(s, "{}", name.len()); - // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how - // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR - // interpretation today will allow skipped arguments to simply not be passed at a call-site. - if !fn_abi.c_variadic { - let mut pushed_arg = false; - for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { - pushed_arg = true; - let ty = arg.layout.ty.fold_with(&mut type_folder); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); - } - if !pushed_arg { - // Empty parameter lists, whether declared as () or conventionally as (void), are - // encoded with a void parameter specifier "v". - typeid.push('v'); - } - } else { - for n in 0..fn_abi.fixed_count as usize { - if fn_abi.args[n].mode == PassMode::Ignore { - continue; + // Prepend a '_' if name starts with a digit or '_' + if let Some(first) = name.as_bytes().first() { + if first.is_ascii_digit() || *first == b'_' { + s.push('_'); } - let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } else { + bug!("encode_ty_name: invalid name `{:?}`", name); } - typeid.push('z'); - } - - // Close the "F..E" pair - typeid.push('E'); - - // Add encoding suffixes - if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - typeid.push_str(".normalized"); - } - - if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) { - typeid.push_str(".generalized"); + s.push_str(&name); } - typeid + s } -/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with -/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. -pub fn typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - mut instance: Instance<'tcx>, - options: TypeIdOptions, -) -> String { - if (matches!(instance.def, ty::InstanceDef::Virtual(..)) - && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn()) - || matches!(instance.def, ty::InstanceDef::DropGlue(..)) - { - // Adjust the type ids of DropGlues - // - // DropGlues may have indirect calls to one or more given types drop function. Rust allows - // for types to be erased to any trait object and retains the drop function for the original - // type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is - // called a second time, it only has information after type erasure and it could be a call - // on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on - // declaration/definition, and during code generation at call sites so they have the same - // type id and match. - // - // FIXME(rcvalle): This allows a drop call on any trait object to call the drop function of - // any other type. - // - let def_id = tcx - .lang_items() - .drop_trait() - .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item")); - let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { - def_id: def_id, - args: List::empty(), - }); - let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); - let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); - instance.args = tcx.mk_args_trait(self_ty, List::empty()); - } else if let ty::InstanceDef::Virtual(def_id, _) = instance.def { - let upcast_ty = match tcx.trait_of_item(def_id) { - Some(trait_id) => trait_object_ty( - tcx, - ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)), - ), - // drop_in_place won't have a defining trait, skip the upcast - None => instance.args.type_at(0), - }; - let stripped_ty = strip_receiver_auto(tcx, upcast_ty); - instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1)); - } else if let ty::InstanceDef::VTableShim(def_id) = instance.def - && let Some(trait_id) = tcx.trait_of_item(def_id) - { - // VTableShims may have a trait method, but a concrete Self. This is not suitable for a vtable, - // as the caller will not know the concrete Self. - let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args); - let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); - instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); - } - - if !options.contains(EncodeTyOptions::USE_CONCRETE_SELF) { - if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) - && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) - { - let impl_method = tcx.associated_item(instance.def_id()); - let method_id = impl_method - .trait_item_def_id - .expect("Part of a trait implementation, but not linked to the def_id?"); - let trait_method = tcx.associated_item(method_id); - let trait_id = trait_ref.skip_binder().def_id; - if traits::is_vtable_safe_method(tcx, trait_id, trait_method) - && tcx.object_safety_violations(trait_id).is_empty() - { - // Trait methods will have a Self polymorphic parameter, where the concreteized - // implementatation will not. We need to walk back to the more general trait method - let trait_ref = tcx.instantiate_and_normalize_erasing_regions( - instance.args, - ty::ParamEnv::reveal_all(), - trait_ref, - ); - let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); - - // At the call site, any call to this concrete function through a vtable will be - // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the - // original method id, and we've recovered the trait arguments, we can make the callee - // instance we're computing the alias set for match the caller instance. - // - // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. - // If we ever *do* start encoding the vtable index, we will need to generate an alias set - // based on which vtables we are putting this method into, as there will be more than one - // index value when supertraits are involved. - instance.def = ty::InstanceDef::Virtual(method_id, 0); - let abstract_trait_args = - tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); - instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); - } - } else if tcx.is_closure_like(instance.def_id()) { - // We're either a closure or a coroutine. Our goal is to find the trait we're defined on, - // instantiate it, and take the type of its only method as our own. - let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); - let (trait_id, inputs) = match closure_ty.kind() { - ty::Closure(..) => { - let closure_args = instance.args.as_closure(); - let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap(); - let tuple_args = - tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0]; - (trait_id, Some(tuple_args)) - } - ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() { - hir::CoroutineKind::Coroutine(..) => ( - tcx.require_lang_item(LangItem::Coroutine, None), - Some(instance.args.as_coroutine().resume_ty()), - ), - hir::CoroutineKind::Desugared(desugaring, _) => { - let lang_item = match desugaring { - hir::CoroutineDesugaring::Async => LangItem::Future, - hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator, - hir::CoroutineDesugaring::Gen => LangItem::Iterator, - }; - (tcx.require_lang_item(lang_item, None), None) - } - }, - ty::CoroutineClosure(..) => ( - tcx.require_lang_item(LangItem::FnOnce, None), - Some( - tcx.instantiate_bound_regions_with_erased( - instance.args.as_coroutine_closure().coroutine_closure_sig(), - ) - .tupled_inputs_ty, - ), - ), - x => bug!("Unexpected type kind for closure-like: {x:?}"), - }; - let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into)); - let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args); - let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); - let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); - // There should be exactly one method on this trait, and it should be the one we're - // defining. - let call = tcx - .associated_items(trait_id) - .in_definition_order() - .find(|it| it.kind == ty::AssocKind::Fn) - .expect("No call-family function on closure-like Fn trait?") - .def_id; - - instance.def = ty::InstanceDef::Virtual(call, 0); - instance.args = abstract_args; - } +/// Converts a number to a disambiguator (see +/// ). +fn to_disambiguator(num: u64) -> String { + if let Some(num) = num.checked_sub(1) { + format!("s{}_", base_n::encode(num as u128, 62)) + } else { + "s_".to_string() } - - let fn_abi = tcx - .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty()))) - .unwrap_or_else(|error| { - bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}") - }); - - typeid_for_fnabi(tcx, fn_abi, options) } -fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty::Dynamic(preds, lifetime, kind) = ty.kind() else { - bug!("Tried to strip auto traits from non-dynamic type {ty}"); - }; - if preds.principal().is_some() { - let filtered_preds = - tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { - !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) - })); - Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind) +/// Converts a number to a sequence number (see +/// ). +fn to_seq_id(num: usize) -> String { + if let Some(num) = num.checked_sub(1) { + base_n::encode(num as u128, 36).to_uppercase() } else { - // If there's no principal type, re-encode it as a unit, since we don't know anything - // about it. This technically discards the knowledge that it was a type that was made - // into a trait object at some point, but that's not a lot. - tcx.types.unit + "".to_string() } } - -#[instrument(skip(tcx), ret)] -fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> { - assert!(!poly_trait_ref.has_non_region_param()); - let principal_pred = poly_trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)) - }); - let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref) - .flat_map(|super_poly_trait_ref| { - tcx.associated_items(super_poly_trait_ref.def_id()) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Type) - .map(move |assoc_ty| { - super_poly_trait_ref.map_bound(|super_trait_ref| { - let alias_ty = ty::AliasTy::new(tcx, assoc_ty.def_id, super_trait_ref.args); - let resolved = tcx.normalize_erasing_regions( - ty::ParamEnv::reveal_all(), - alias_ty.to_ty(tcx), - ); - debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx)); - ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - def_id: assoc_ty.def_id, - args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args, - term: resolved.into(), - }) - }) - }) - }) - .collect(); - assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); - let preds = tcx.mk_poly_existential_predicates_from_iter( - iter::once(principal_pred).chain(assoc_preds.into_iter()), - ); - Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn) -} diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs new file mode 100644 index 0000000000000..b6182dc4e635d --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs @@ -0,0 +1,123 @@ +//! Type metadata identifiers (using Itanium C++ ABI mangling for encoding) for LLVM Control Flow +//! Integrity (CFI) and cross-language LLVM CFI support. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::bug; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_target::abi::call::{Conv, FnAbi, PassMode}; +use tracing::instrument; + +mod encode; +mod transform; +use crate::cfi::typeid::itanium_cxx_abi::encode::{encode_ty, DictKey, EncodeTyOptions}; +use crate::cfi::typeid::itanium_cxx_abi::transform::{ + transform_instance, TransformTy, TransformTyOptions, +}; +use crate::cfi::typeid::TypeIdOptions; + +/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor +/// extended type qualifiers and types for Rust types that are not used at the FFI boundary. +#[instrument(level = "trace", skip(tcx))] +pub fn typeid_for_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions + // its type. + let mut typeid = String::from("_Z"); + + // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type + // metadata identifiers for function pointers. The typeinfo name encoding is a two-character + // code (i.e., 'TS') prefixed to the type encoding for the function. + typeid.push_str("TS"); + + // Function types are delimited by an "F..E" pair + typeid.push('F'); + + // A dictionary of substitution candidates used for compression (see + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). + let mut dict: FxHashMap, usize> = FxHashMap::default(); + + let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); + match fn_abi.conv { + Conv::C => { + encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); + } + _ => { + encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); + } + } + + // Encode the return type + let transform_ty_options = TransformTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); + let mut type_folder = TransformTy::new(tcx, transform_ty_options); + let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + + // Encode the parameter types + + // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how + // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR + // interpretation today will allow skipped arguments to simply not be passed at a call-site. + if !fn_abi.c_variadic { + let mut pushed_arg = false; + for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { + pushed_arg = true; + let ty = arg.layout.ty.fold_with(&mut type_folder); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + if !pushed_arg { + // Empty parameter lists, whether declared as () or conventionally as (void), are + // encoded with a void parameter specifier "v". + typeid.push('v'); + } + } else { + for n in 0..fn_abi.fixed_count as usize { + if fn_abi.args[n].mode == PassMode::Ignore { + continue; + } + let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + + typeid.push('z'); + } + + // Close the "F..E" pair + typeid.push('E'); + + // Add encoding suffixes + if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + typeid.push_str(".normalized"); + } + + if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) { + typeid.push_str(".generalized"); + } + + typeid +} + +/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with +/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. +#[instrument(level = "trace", skip(tcx))] +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + options: TypeIdOptions, +) -> String { + let transform_ty_options = TransformTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_instance: invalid option(s) `{:?}`", options.bits())); + let instance = transform_instance(tcx, instance, transform_ty_options); + let fn_abi = tcx + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty()))) + .unwrap_or_else(|error| { + bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}") + }); + typeid_for_fnabi(tcx, fn_abi, options) +} diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs new file mode 100644 index 0000000000000..7141c6c9bb0c3 --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -0,0 +1,450 @@ +//! Transforms instances and types for LLVM CFI and cross-language LLVM CFI support using Itanium +//! C++ ABI mangling. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +use rustc_hir as hir; +use rustc_hir::LangItem; +use rustc_middle::bug; +use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::{ + self, Instance, IntTy, List, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, UintTy, +}; +use rustc_span::sym; +use rustc_trait_selection::traits; +use std::iter; +use tracing::{debug, instrument}; + +use crate::cfi::typeid::itanium_cxx_abi::encode::EncodeTyOptions; +use crate::cfi::typeid::TypeIdOptions; + +/// Options for transform_ty. +pub type TransformTyOptions = TypeIdOptions; + +pub struct TransformTy<'tcx> { + tcx: TyCtxt<'tcx>, + options: TransformTyOptions, + parents: Vec>, +} + +impl<'tcx> TransformTy<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self { + TransformTy { tcx, options, parents: Vec::new() } + } +} + +/// Transforms a ty:Ty for being encoded and used in the substitution dictionary. +/// +/// * Transforms all c_void types into unit types. +/// * Generalizes pointers if TransformTyOptions::GENERALIZE_POINTERS option is set. +/// * Normalizes integers if TransformTyOptions::NORMALIZE_INTEGERS option is set. +/// * Generalizes any repr(transparent) user-defined type that is either a pointer or reference, and +/// either references itself or any other type that contains or references itself, to avoid a +/// reference cycle. +/// * Transforms repr(transparent) types without non-ZST field into (). +/// +impl<'tcx> TypeFolder> for TransformTy<'tcx> { + // Transforms a ty:Ty for being encoded and used in the substitution dictionary. + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.kind() { + ty::Array(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineClosure(..) + | ty::CoroutineWitness(..) + | ty::Dynamic(..) + | ty::Float(..) + | ty::FnDef(..) + | ty::Foreign(..) + | ty::Never + | ty::Pat(..) + | ty::Slice(..) + | ty::Str + | ty::Tuple(..) => t.super_fold_with(self), + + ty::Bool => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Note: on all platforms that Rust's currently supports, its size and alignment + // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs. + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) + // + // Clang represents bool as an 8-bit unsigned integer. + self.tcx.types.u8 + } else { + t + } + } + + ty::Char => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Since #118032, char is guaranteed to have the same size, alignment, and + // function call ABI as u32 on all platforms. + self.tcx.types.u32 + } else { + t + } + } + + ty::Int(..) | ty::Uint(..) => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit + // wide. All platforms we currently support have a C platform, and as a + // consequence, isize/usize are at least 16-bit wide for all of them. + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.) + match t.kind() { + ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width { + 16 => self.tcx.types.i16, + 32 => self.tcx.types.i32, + 64 => self.tcx.types.i64, + 128 => self.tcx.types.i128, + _ => bug!( + "fold_ty: unexpected pointer width `{}`", + self.tcx.sess.target.pointer_width + ), + }, + ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width { + 16 => self.tcx.types.u16, + 32 => self.tcx.types.u32, + 64 => self.tcx.types.u64, + 128 => self.tcx.types.u128, + _ => bug!( + "fold_ty: unexpected pointer width `{}`", + self.tcx.sess.target.pointer_width + ), + }, + _ => t, + } + } else { + t + } + } + + ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit, + + ty::Adt(adt_def, args) => { + if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t) + { + // Don't transform repr(transparent) types with an user-defined CFI encoding to + // preserve the user-defined CFI encoding. + if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) { + return t; + } + let variant = adt_def.non_enum_variant(); + let param_env = self.tcx.param_env(variant.def_id); + let field = variant.fields.iter().find(|field| { + let ty = self.tcx.type_of(field.did).instantiate_identity(); + let is_zst = self + .tcx + .layout_of(param_env.and(ty)) + .is_ok_and(|layout| layout.is_zst()); + !is_zst + }); + if let Some(field) = field { + let ty0 = self.tcx.type_of(field.did).instantiate(self.tcx, args); + // Generalize any repr(transparent) user-defined type that is either a + // pointer or reference, and either references itself or any other type that + // contains or references itself, to avoid a reference cycle. + + // If the self reference is not through a pointer, for example, due + // to using `PhantomData`, need to skip normalizing it if we hit it again. + self.parents.push(t); + let ty = if ty0.is_any_ptr() && ty0.contains(t) { + let options = self.options; + self.options |= TransformTyOptions::GENERALIZE_POINTERS; + let ty = ty0.fold_with(self); + self.options = options; + ty + } else { + ty0.fold_with(self) + }; + self.parents.pop(); + ty + } else { + // Transform repr(transparent) types without non-ZST field into () + self.tcx.types.unit + } + } else { + t.super_fold_with(self) + } + } + + ty::Ref(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if t.is_mutable_ptr() { + Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) + } else { + Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) + } + } else { + t.super_fold_with(self) + } + } + + ty::RawPtr(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if t.is_mutable_ptr() { + Ty::new_mut_ptr(self.tcx, self.tcx.types.unit) + } else { + Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) + } + } else { + t.super_fold_with(self) + } + } + + ty::FnPtr(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) + } else { + t.super_fold_with(self) + } + } + + ty::Alias(..) => { + self.fold_ty(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t)) + } + + ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { + bug!("fold_ty: unexpected `{:?}`", t.kind()); + } + } + } + + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } +} + +#[instrument(skip(tcx), ret)] +fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> { + assert!(!poly_trait_ref.has_non_region_param()); + let principal_pred = poly_trait_ref.map_bound(|trait_ref| { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)) + }); + let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref) + .flat_map(|super_poly_trait_ref| { + tcx.associated_items(super_poly_trait_ref.def_id()) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Type) + .map(move |assoc_ty| { + super_poly_trait_ref.map_bound(|super_trait_ref| { + let alias_ty = ty::AliasTy::new(tcx, assoc_ty.def_id, super_trait_ref.args); + let resolved = tcx.normalize_erasing_regions( + ty::ParamEnv::reveal_all(), + alias_ty.to_ty(tcx), + ); + debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx)); + ty::ExistentialPredicate::Projection(ty::ExistentialProjection { + def_id: assoc_ty.def_id, + args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args, + term: resolved.into(), + }) + }) + }) + }) + .collect(); + assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + let preds = tcx.mk_poly_existential_predicates_from_iter( + iter::once(principal_pred).chain(assoc_preds.into_iter()), + ); + Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn) +} + +/// Transforms an instance for LLVM CFI and cross-language LLVM CFI support using Itanium C++ ABI +/// mangling. +/// +/// typeid_for_instance is called at two locations, initially when declaring/defining functions and +/// methods, and later during code generation at call sites, after type erasure might have ocurred. +/// +/// In the first call (i.e., when declaring/defining functions and methods), it encodes type ids for +/// an FnAbi or Instance, and these type ids are attached to functions and methods. (These type ids +/// are used later by the LowerTypeTests LLVM pass to aggregate functions in groups derived from +/// these type ids.) +/// +/// In the second call (i.e., during code generation at call sites), it encodes a type id for an +/// FnAbi or Instance, after type erasure might have occured, and this type id is used for testing +/// if a function is member of the group derived from this type id. Therefore, in the first call to +/// typeid_for_fnabi (when type ids are attached to functions and methods), it can only include at +/// most as much information that would be available in the second call (i.e., during code +/// generation at call sites); otherwise, the type ids would not not match. +/// +/// For this, it: +/// +/// * Adjust the type ids of DropGlues (see below). +/// * Adjusts the type ids of VTableShims to the type id expected in the call sites for the +/// entry in the vtable (i.e., by using the signature of the closure passed as an argument to the +/// shim, or by just removing self). +/// * Performs type erasure for calls on trait objects by transforming self into a trait object of +/// the trait that defines the method. +/// * Performs type erasure for closures call methods by transforming self into a trait object of +/// the Fn trait that defines the method (for being attached as a secondary type id). +/// +#[instrument(level = "trace", skip(tcx))] +pub fn transform_instance<'tcx>( + tcx: TyCtxt<'tcx>, + mut instance: Instance<'tcx>, + options: TransformTyOptions, +) -> Instance<'tcx> { + if (matches!(instance.def, ty::InstanceDef::Virtual(..)) + && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn()) + || matches!(instance.def, ty::InstanceDef::DropGlue(..)) + { + // Adjust the type ids of DropGlues + // + // DropGlues may have indirect calls to one or more given types drop function. Rust allows + // for types to be erased to any trait object and retains the drop function for the original + // type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is + // called a second time, it only has information after type erasure and it could be a call + // on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on + // declaration/definition, and during code generation at call sites so they have the same + // type id and match. + // + // FIXME(rcvalle): This allows a drop call on any trait object to call the drop function of + // any other type. + // + let def_id = tcx + .lang_items() + .drop_trait() + .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item")); + let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { + def_id: def_id, + args: List::empty(), + }); + let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); + let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); + instance.args = tcx.mk_args_trait(self_ty, List::empty()); + } else if let ty::InstanceDef::Virtual(def_id, _) = instance.def { + // Transform self into a trait object of the trait that defines the method for virtual + // functions to match the type erasure done below. + let upcast_ty = match tcx.trait_of_item(def_id) { + Some(trait_id) => trait_object_ty( + tcx, + ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)), + ), + // drop_in_place won't have a defining trait, skip the upcast + None => instance.args.type_at(0), + }; + let ty::Dynamic(preds, lifetime, kind) = upcast_ty.kind() else { + bug!("Tried to remove autotraits from non-dynamic type {upcast_ty}"); + }; + let self_ty = if preds.principal().is_some() { + let filtered_preds = + tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { + !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) + })); + Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind) + } else { + // If there's no principal type, re-encode it as a unit, since we don't know anything + // about it. This technically discards the knowledge that it was a type that was made + // into a trait object at some point, but that's not a lot. + tcx.types.unit + }; + instance.args = tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1)); + } else if let ty::InstanceDef::VTableShim(def_id) = instance.def + && let Some(trait_id) = tcx.trait_of_item(def_id) + { + // Adjust the type ids of VTableShims to the type id expected in the call sites for the + // entry in the vtable (i.e., by using the signature of the closure passed as an argument + // to the shim, or by just removing self). + let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + } + + if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) { + // Perform type erasure for calls on trait objects by transforming self into a trait object + // of the trait that defines the method. + if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) + && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) + { + let impl_method = tcx.associated_item(instance.def_id()); + let method_id = impl_method + .trait_item_def_id + .expect("Part of a trait implementation, but not linked to the def_id?"); + let trait_method = tcx.associated_item(method_id); + let trait_id = trait_ref.skip_binder().def_id; + if traits::is_vtable_safe_method(tcx, trait_id, trait_method) + && tcx.object_safety_violations(trait_id).is_empty() + { + // Trait methods will have a Self polymorphic parameter, where the concreteized + // implementatation will not. We need to walk back to the more general trait method + let trait_ref = tcx.instantiate_and_normalize_erasing_regions( + instance.args, + ty::ParamEnv::reveal_all(), + trait_ref, + ); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + + // At the call site, any call to this concrete function through a vtable will be + // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the + // original method id, and we've recovered the trait arguments, we can make the callee + // instance we're computing the alias set for match the caller instance. + // + // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. + // If we ever *do* start encoding the vtable index, we will need to generate an alias set + // based on which vtables we are putting this method into, as there will be more than one + // index value when supertraits are involved. + instance.def = ty::InstanceDef::Virtual(method_id, 0); + let abstract_trait_args = + tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); + } + } else if tcx.is_closure_like(instance.def_id()) { + // We're either a closure or a coroutine. Our goal is to find the trait we're defined on, + // instantiate it, and take the type of its only method as our own. + let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); + let (trait_id, inputs) = match closure_ty.kind() { + ty::Closure(..) => { + let closure_args = instance.args.as_closure(); + let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap(); + let tuple_args = + tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0]; + (trait_id, Some(tuple_args)) + } + ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() { + hir::CoroutineKind::Coroutine(..) => ( + tcx.require_lang_item(LangItem::Coroutine, None), + Some(instance.args.as_coroutine().resume_ty()), + ), + hir::CoroutineKind::Desugared(desugaring, _) => { + let lang_item = match desugaring { + hir::CoroutineDesugaring::Async => LangItem::Future, + hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator, + hir::CoroutineDesugaring::Gen => LangItem::Iterator, + }; + (tcx.require_lang_item(lang_item, None), None) + } + }, + ty::CoroutineClosure(..) => ( + tcx.require_lang_item(LangItem::FnOnce, None), + Some( + tcx.instantiate_bound_regions_with_erased( + instance.args.as_coroutine_closure().coroutine_closure_sig(), + ) + .tupled_inputs_ty, + ), + ), + x => bug!("Unexpected type kind for closure-like: {x:?}"), + }; + let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into)); + let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + // There should be exactly one method on this trait, and it should be the one we're + // defining. + let call = tcx + .associated_items(trait_id) + .in_definition_order() + .find(|it| it.kind == ty::AssocKind::Fn) + .expect("No call-family function on closure-like Fn trait?") + .def_id; + + instance.def = ty::InstanceDef::Virtual(call, 0); + instance.args = abstract_args; + } + } + + instance +} diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs new file mode 100644 index 0000000000000..ad8b180443999 --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs @@ -0,0 +1,54 @@ +//! Type metadata identifiers for LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI +//! support for the Rust compiler. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +use bitflags::bitflags; +use rustc_middle::ty::{Instance, Ty, TyCtxt}; +use rustc_target::abi::call::FnAbi; + +bitflags! { + /// Options for typeid_for_fnabi. + #[derive(Clone, Copy, Debug)] + pub struct TypeIdOptions: u32 { + /// Generalizes pointers for compatibility with Clang + /// `-fsanitize-cfi-icall-generalize-pointers` option for cross-language LLVM CFI and KCFI + /// support. + const GENERALIZE_POINTERS = 1; + /// Generalizes repr(C) user-defined type for extern function types with the "C" calling + /// convention (or extern types) for cross-language LLVM CFI and KCFI support. + const GENERALIZE_REPR_C = 2; + /// Normalizes integers for compatibility with Clang + /// `-fsanitize-cfi-icall-experimental-normalize-integers` option for cross-language LLVM + /// CFI and KCFI support. + const NORMALIZE_INTEGERS = 4; + /// Do not perform self type erasure for attaching a secondary type id to methods with their + /// concrete self so they can be used as function pointers. + /// + /// (This applies to typeid_for_instance only and should be used to attach a secondary type + /// id to methods during their declaration/definition so they match the type ids returned by + /// either typeid_for_instance or typeid_for_fnabi at call sites during code generation for + /// type membership tests when methods are used as function pointers.) + const USE_CONCRETE_SELF = 8; + } +} + +pub mod itanium_cxx_abi; + +/// Returns a type metadata identifier for the specified FnAbi. +pub fn typeid_for_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) +} + +/// Returns a type metadata identifier for the specified Instance. +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + options: TypeIdOptions, +) -> String { + itanium_cxx_abi::typeid_for_instance(tcx, instance, options) +} diff --git a/compiler/rustc_sanitizers/src/kcfi/mod.rs b/compiler/rustc_sanitizers/src/kcfi/mod.rs new file mode 100644 index 0000000000000..a8b632940b040 --- /dev/null +++ b/compiler/rustc_sanitizers/src/kcfi/mod.rs @@ -0,0 +1,7 @@ +//! LLVM Kernel Control Flow Integrity (KCFI) and cross-language LLVM KCFI support for the Rust +//! compiler. +//! +//! For more information about LLVM KCFI and cross-language LLVM KCFI support for the Rust compiler, +//! see the tracking issue #123479. +pub mod typeid; +pub use crate::kcfi::typeid::{typeid_for_fnabi, typeid_for_instance, TypeIdOptions}; diff --git a/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs new file mode 100644 index 0000000000000..436c398e39b06 --- /dev/null +++ b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs @@ -0,0 +1,55 @@ +//! Type metadata identifiers for LLVM Kernel Control Flow Integrity (KCFI) and cross-language LLVM +//! KCFI support for the Rust compiler. +//! +//! For more information about LLVM KCFI and cross-language LLVM KCFI support for the Rust compiler, +//! see the tracking issue #123479. +use rustc_middle::ty::{Instance, InstanceDef, ReifyReason, Ty, TyCtxt}; +use rustc_target::abi::call::FnAbi; +use std::hash::Hasher; +use twox_hash::XxHash64; + +pub use crate::cfi::typeid::{itanium_cxx_abi, TypeIdOptions}; + +/// Returns a KCFI type metadata identifier for the specified FnAbi. +pub fn typeid_for_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> u32 { + // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the + // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) + let mut hash: XxHash64 = Default::default(); + hash.write(itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes()); + hash.finish() as u32 +} + +/// Returns a KCFI type metadata identifier for the specified Instance. +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + mut options: TypeIdOptions, +) -> u32 { + // KCFI support for Rust shares most of its implementation with the CFI support, with some key + // differences: + // + // 1. KCFI performs type tests differently and are implemented as different LLVM passes than CFI + // to not require LTO. + // 2. KCFI has the limitation that a function or method may have one type id assigned only. + // + // Because of the limitation listed above (2), the current KCFI implementation (not CFI) does + // reifying of types (i.e., adds shims/trampolines for indirect calls in these cases) for: + // + // * Supporting casting between function items, closures, and Fn trait objects. + // * Supporting methods being cast as function pointers. + // + // This was implemented for KCFI support in #123106 and #123052 (which introduced the + // ReifyReason). The tracking issue for KCFI support for Rust is #123479. + if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) { + options.insert(TypeIdOptions::USE_CONCRETE_SELF); + } + // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the + // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) + let mut hash: XxHash64 = Default::default(); + hash.write(itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); + hash.finish() as u32 +} diff --git a/compiler/rustc_sanitizers/src/lib.rs b/compiler/rustc_sanitizers/src/lib.rs new file mode 100644 index 0000000000000..1f73e255490b2 --- /dev/null +++ b/compiler/rustc_sanitizers/src/lib.rs @@ -0,0 +1,7 @@ +#![feature(let_chains)] +//! Sanitizers support for the Rust compiler. +//! +//! This crate contains the source code for providing support for the sanitizers to the Rust +//! compiler. +pub mod cfi; +pub mod kcfi; diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index a904cd10041cb..0893bc31bfacd 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -10,7 +10,7 @@ use rustc_span::Symbol; use stable_mir::abi::Layout; use stable_mir::mir::alloc::AllocId; use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; -use stable_mir::mir::{Mutability, Place, ProjectionElem, Safety}; +use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety}; use stable_mir::ty::{ Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, @@ -551,6 +551,38 @@ impl RustcInternal for ProjectionElem { } } +impl RustcInternal for BinOp { + type T<'tcx> = rustc_middle::mir::BinOp; + + fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + match self { + BinOp::Add => rustc_middle::mir::BinOp::Add, + BinOp::AddUnchecked => rustc_middle::mir::BinOp::AddUnchecked, + BinOp::Sub => rustc_middle::mir::BinOp::Sub, + BinOp::SubUnchecked => rustc_middle::mir::BinOp::SubUnchecked, + BinOp::Mul => rustc_middle::mir::BinOp::Mul, + BinOp::MulUnchecked => rustc_middle::mir::BinOp::MulUnchecked, + BinOp::Div => rustc_middle::mir::BinOp::Div, + BinOp::Rem => rustc_middle::mir::BinOp::Rem, + BinOp::BitXor => rustc_middle::mir::BinOp::BitXor, + BinOp::BitAnd => rustc_middle::mir::BinOp::BitAnd, + BinOp::BitOr => rustc_middle::mir::BinOp::BitOr, + BinOp::Shl => rustc_middle::mir::BinOp::Shl, + BinOp::ShlUnchecked => rustc_middle::mir::BinOp::ShlUnchecked, + BinOp::Shr => rustc_middle::mir::BinOp::Shr, + BinOp::ShrUnchecked => rustc_middle::mir::BinOp::ShrUnchecked, + BinOp::Eq => rustc_middle::mir::BinOp::Eq, + BinOp::Lt => rustc_middle::mir::BinOp::Lt, + BinOp::Le => rustc_middle::mir::BinOp::Le, + BinOp::Ne => rustc_middle::mir::BinOp::Ne, + BinOp::Ge => rustc_middle::mir::BinOp::Ge, + BinOp::Gt => rustc_middle::mir::BinOp::Gt, + BinOp::Cmp => rustc_middle::mir::BinOp::Cmp, + BinOp::Offset => rustc_middle::mir::BinOp::Offset, + } + } +} + impl RustcInternal for &T where T: RustcInternal, diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 7c12168b80951..61bbedf9eec9d 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -19,7 +19,7 @@ use stable_mir::abi::{FnAbi, Layout, LayoutShape}; use stable_mir::compiler_interface::Context; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::{InstanceDef, StaticDef}; -use stable_mir::mir::{Body, Place}; +use stable_mir::mir::{BinOp, Body, Place}; use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, @@ -668,6 +668,15 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let tcx = tables.tcx; format!("{:?}", place.internal(&mut *tables, tcx)) } + + fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let rhs_internal = rhs.internal(&mut *tables, tcx); + let lhs_internal = lhs.internal(&mut *tables, tcx); + let ty = bin_op.internal(&mut *tables, tcx).ty(tcx, rhs_internal, lhs_internal); + ty.stable(&mut *tables) + } } pub struct TablesWrapper<'tcx>(pub RefCell>); diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index 1c8f1d0367039..65aa9e40c8ba3 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -bitflags = "2.4.1" punycode = "0.4.0" rustc-demangle = "0.1.21" rustc_data_structures = { path = "../rustc_data_structures" } @@ -15,7 +14,5 @@ rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -rustc_trait_selection = { path = "../rustc_trait_selection" } tracing = "0.1" -twox-hash = "1.6.3" # tidy-alphabetical-end diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 0588af9bda72a..b950947870272 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -114,7 +114,6 @@ mod v0; pub mod errors; pub mod test; -pub mod typeid; /// This function computes the symbol name for the given `instance` and the /// given instantiating crate. That is, if you know that instance X is diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs deleted file mode 100644 index 7bd998294dd5a..0000000000000 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ /dev/null @@ -1,100 +0,0 @@ -/// Type metadata identifiers for LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI -/// support. -/// -/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, -/// see design document in the tracking issue #89653. -use bitflags::bitflags; -use rustc_middle::ty::{Instance, InstanceDef, ReifyReason, Ty, TyCtxt}; -use rustc_target::abi::call::FnAbi; -use std::hash::Hasher; -use twox_hash::XxHash64; - -bitflags! { - /// Options for typeid_for_fnabi. - #[derive(Clone, Copy, Debug)] - pub struct TypeIdOptions: u32 { - /// Generalizes pointers for compatibility with Clang - /// `-fsanitize-cfi-icall-generalize-pointers` option for cross-language LLVM CFI and KCFI - /// support. - const GENERALIZE_POINTERS = 1; - /// Generalizes repr(C) user-defined type for extern function types with the "C" calling - /// convention (or extern types) for cross-language LLVM CFI and KCFI support. - const GENERALIZE_REPR_C = 2; - /// Normalizes integers for compatibility with Clang - /// `-fsanitize-cfi-icall-experimental-normalize-integers` option for cross-language LLVM - /// CFI and KCFI support. - const NORMALIZE_INTEGERS = 4; - /// Do not perform self type erasure for attaching a secondary type id to methods with their - /// concrete self so they can be used as function pointers. - /// - /// (This applies to typeid_for_instance only and should be used to attach a secondary type - /// id to methods during their declaration/definition so they match the type ids returned by - /// either typeid_for_instance or typeid_for_fnabi at call sites during code generation for - /// type membership tests when methods are used as function pointers.) - const USE_CONCRETE_SELF = 8; - } -} - -mod typeid_itanium_cxx_abi; - -/// Returns a type metadata identifier for the specified FnAbi. -pub fn typeid_for_fnabi<'tcx>( - tcx: TyCtxt<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, -) -> String { - typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) -} - -/// Returns a type metadata identifier for the specified Instance. -pub fn typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - options: TypeIdOptions, -) -> String { - typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options) -} - -/// Returns a KCFI type metadata identifier for the specified FnAbi. -pub fn kcfi_typeid_for_fnabi<'tcx>( - tcx: TyCtxt<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, -) -> u32 { - // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the - // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) - let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes()); - hash.finish() as u32 -} - -/// Returns a KCFI type metadata identifier for the specified Instance. -pub fn kcfi_typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - mut options: TypeIdOptions, -) -> u32 { - // KCFI support for Rust shares most of its implementation with the CFI support, with some key - // differences: - // - // 1. KCFI performs type tests differently and are implemented as different LLVM passes than CFI - // to not require LTO. - // 2. KCFI has the limitation that a function or method may have one type id assigned only. - // - // Because of the limitation listed above (2), the current KCFI implementation (not CFI) does - // reifying of types (i.e., adds shims/trampolines for indirect calls in these cases) for: - // - // * Supporting casting between function items, closures, and Fn trait objects. - // * Supporting methods being cast as function pointers. - // - // This was implemented for KCFI support in #123106 and #123052 (which introduced the - // ReifyReason). The tracking issue for KCFI support for Rust is #123479. - if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) { - options.insert(TypeIdOptions::USE_CONCRETE_SELF); - } - // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the - // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) - let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); - hash.finish() as u32 -} diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 0199e225c5f83..cb255fabfe21a 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -45,8 +45,8 @@ pub(super) fn mangle<'tcx>( ty::InstanceDef::ThreadLocalShim(_) => Some("tls"), ty::InstanceDef::VTableShim(_) => Some("vtable"), ty::InstanceDef::ReifyShim(_, None) => Some("reify"), - ty::InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify-fnptr"), - ty::InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify-vtable"), + ty::InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify_fnptr"), + ty::InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify_vtable"), ty::InstanceDef::ConstructCoroutineInClosureShim { .. } | ty::InstanceDef::CoroutineKindShim { .. } => Some("fn_once"), diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index 8ed34fab54d09..94c552199bc5f 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -8,7 +8,7 @@ use std::cell::Cell; use crate::abi::{FnAbi, Layout, LayoutShape}; use crate::mir::alloc::{AllocId, GlobalAlloc}; use crate::mir::mono::{Instance, InstanceDef, StaticDef}; -use crate::mir::{Body, Place}; +use crate::mir::{BinOp, Body, Place}; use crate::target::MachineInfo; use crate::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, @@ -211,6 +211,9 @@ pub trait Context { /// Get a debug string representation of a place. fn place_pretty(&self, place: &Place) -> String; + + /// Get the resulting type of binary operation. + fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty; } // A thread local variable that stores a pointer to the tables mapping between TyCtxt diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 593b1868f26fa..1ad05633d62dc 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -1,3 +1,4 @@ +use crate::compiler_interface::with; use crate::mir::pretty::function_body; use crate::ty::{ AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, RigidTy, Ty, TyKind, @@ -337,42 +338,7 @@ impl BinOp { /// Return the type of this operation for the given input Ty. /// This function does not perform type checking, and it currently doesn't handle SIMD. pub fn ty(&self, lhs_ty: Ty, rhs_ty: Ty) -> Ty { - match self { - BinOp::Add - | BinOp::AddUnchecked - | BinOp::Sub - | BinOp::SubUnchecked - | BinOp::Mul - | BinOp::MulUnchecked - | BinOp::Div - | BinOp::Rem - | BinOp::BitXor - | BinOp::BitAnd - | BinOp::BitOr => { - assert_eq!(lhs_ty, rhs_ty); - assert!(lhs_ty.kind().is_primitive()); - lhs_ty - } - BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => { - assert!(lhs_ty.kind().is_primitive()); - assert!(rhs_ty.kind().is_primitive()); - lhs_ty - } - BinOp::Offset => { - assert!(lhs_ty.kind().is_raw_ptr()); - assert!(rhs_ty.kind().is_integral()); - lhs_ty - } - BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { - assert_eq!(lhs_ty, rhs_ty); - let lhs_kind = lhs_ty.kind(); - assert!(lhs_kind.is_primitive() || lhs_kind.is_raw_ptr() || lhs_kind.is_fn_ptr()); - Ty::bool_ty() - } - BinOp::Cmp => { - unimplemented!("Should cmp::Ordering be a RigidTy?"); - } - } + with(|ctx| ctx.binop_ty(*self, lhs_ty, rhs_ty)) } } diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index 6f72056989818..a3e8f6176a3ba 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -78,16 +78,6 @@ ENV PATH="$NODE_FOLDER:${PATH}" COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/ -# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries -# to create a new folder. For reference: -# https://github.com/puppeteer/puppeteer/issues/375 -# -# We also specify the version in case we need to update it to go around cache limitations. -# -# The `browser-ui-test.version` file is also used by bootstrap to emit warnings in case -# the local version of the package is different than the one used by the CI. -RUN npm install -g browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true - ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --save-toolstates=/tmp/toolstate/toolstates.json \ @@ -100,6 +90,14 @@ COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts +# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries +# to create a new folder. For reference: +# https://github.com/puppeteer/puppeteer/issues/375 +# +# We also specify the version in case we need to update it to go around cache limitations. +# +# The `browser-ui-test.version` file is also used by bootstrap to emit warnings in case +# the local version of the package is different than the one used by the CI. ENV SCRIPT /tmp/checktools.sh ../x.py && \ - NODE_PATH=`npm root -g` python3 ../x.py test tests/rustdoc-gui --stage 2 \ - --test-args "'--no-sandbox --jobs 1'" + npm install browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true && \ + python3 ../x.py test tests/rustdoc-gui --stage 2 --test-args "'--no-sandbox --jobs 1'" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 14a8c24575690..50c2e5e29f0b8 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.17.1 \ No newline at end of file +0.17.2 \ No newline at end of file diff --git a/src/doc/book b/src/doc/book index 19c40bfd2d576..3131aa4642c62 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 19c40bfd2d57641d962f3119a1c343355f1b3c5e +Subproject commit 3131aa4642c627a24f523c82566b94a7d920f68c diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 98b33e9a44145..eb3eb80e106d0 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 98b33e9a441457b0a491fe1be90e7de64eafc3e5 +Subproject commit eb3eb80e106d03250c1fb7c5666b1c8c59672862 diff --git a/src/doc/embedded-book b/src/doc/embedded-book index 2e95fc2fd31d6..aa7d4b0b4653d 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit 2e95fc2fd31d669947e993aa07ef10dc9828bee7 +Subproject commit aa7d4b0b4653ddb47cb1de2036d090ec2ba9dab1 diff --git a/src/doc/nomicon b/src/doc/nomicon index 6bc2415218d4d..0d5f88475fe28 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 6bc2415218d4dd0cb01433d8320f5ccf79c343a1 +Subproject commit 0d5f88475fe285affa6dbbc806e9e44d730797c0 diff --git a/src/doc/reference b/src/doc/reference index 984b36eca4b92..55694913b1301 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 984b36eca4b9293df04d5ba4eb5c4f77db0f51dc +Subproject commit 55694913b1301cc809f9bf4a1ad1b3d6920efbd9 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 7601e0c5ad29d..60d34b5fd33db 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 7601e0c5ad29d5bd3b518700ea63fddfff5915a7 +Subproject commit 60d34b5fd33db1346f9aabfc0c9d0bda6c8e42be diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index ffa246b7fd95a..b77a34bd46399 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit ffa246b7fd95a96e1cd54883e613aed42c32547d +Subproject commit b77a34bd46399687b4ce6a17198e9f316c988794 diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 72d4cc7c4659c..8ed6ee014f3fa 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -1,141 +1,130 @@ -use crate::rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_hir as hir; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TyCtxtInferExt}; use rustc_infer::traits; -use rustc_middle::ty::ToPredicate; +use rustc_middle::ty::{self, ToPredicate}; +use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use super::*; +use thin_vec::ThinVec; -pub(crate) struct BlanketImplFinder<'a, 'tcx> { - pub(crate) cx: &'a mut core::DocContext<'tcx>, -} +use crate::clean; +use crate::clean::{ + clean_middle_assoc_item, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty_generics, +}; +use crate::core::DocContext; + +#[instrument(level = "debug", skip(cx))] +pub(crate) fn synthesize_blanket_impls( + cx: &mut DocContext<'_>, + item_def_id: DefId, +) -> Vec { + let tcx = cx.tcx; + let ty = tcx.type_of(item_def_id); -impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { - pub(crate) fn get_blanket_impls(&mut self, item_def_id: DefId) -> Vec { - let cx = &mut self.cx; - let ty = cx.tcx.type_of(item_def_id); + let mut blanket_impls = Vec::new(); + for trait_def_id in tcx.all_traits() { + if !cx.cache.effective_visibilities.is_reachable(tcx, trait_def_id) + || cx.generated_synthetics.get(&(ty.skip_binder(), trait_def_id)).is_some() + { + continue; + } + // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls + let trait_impls = tcx.trait_impls_of(trait_def_id); + 'blanket_impls: for &impl_def_id in trait_impls.blanket_impls() { + trace!("considering impl `{impl_def_id:?}` for trait `{trait_def_id:?}`"); - trace!("get_blanket_impls({ty:?})"); - let mut impls = Vec::new(); - for trait_def_id in cx.tcx.all_traits() { - if !cx.cache.effective_visibilities.is_reachable(cx.tcx, trait_def_id) - || cx.generated_synthetics.get(&(ty.skip_binder(), trait_def_id)).is_some() - { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { continue; } - // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls - let trait_impls = cx.tcx.trait_impls_of(trait_def_id); - 'blanket_impls: for &impl_def_id in trait_impls.blanket_impls() { - trace!( - "get_blanket_impls: Considering impl for trait '{:?}' {:?}", - trait_def_id, - impl_def_id - ); - let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap(); - if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { - continue; - } - let infcx = cx.tcx.infer_ctxt().build(); - let args = infcx.fresh_args_for_item(DUMMY_SP, item_def_id); - let impl_ty = ty.instantiate(infcx.tcx, args); - let param_env = ty::ParamEnv::empty(); + let infcx = tcx.infer_ctxt().build(); + let args = infcx.fresh_args_for_item(DUMMY_SP, item_def_id); + let impl_ty = ty.instantiate(tcx, args); + let param_env = ty::ParamEnv::empty(); - let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); - let impl_trait_ref = trait_ref.instantiate(infcx.tcx, impl_args); + let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = trait_ref.instantiate(tcx, impl_args); - // Require the type the impl is implemented on to match - // our type, and ignore the impl if there was a mismatch. - let Ok(eq_result) = infcx.at(&traits::ObligationCause::dummy(), param_env).eq( - DefineOpaqueTypes::Yes, - impl_trait_ref.self_ty(), - impl_ty, - ) else { - continue; - }; - let InferOk { value: (), obligations } = eq_result; - // FIXME(eddyb) ignoring `obligations` might cause false positives. - drop(obligations); + // Require the type the impl is implemented on to match + // our type, and ignore the impl if there was a mismatch. + let Ok(eq_result) = infcx.at(&traits::ObligationCause::dummy(), param_env).eq( + DefineOpaqueTypes::Yes, + impl_trait_ref.self_ty(), + impl_ty, + ) else { + continue; + }; + let InferOk { value: (), obligations } = eq_result; + // FIXME(eddyb) ignoring `obligations` might cause false positives. + drop(obligations); - trace!( - "invoking predicate_may_hold: param_env={:?}, impl_trait_ref={:?}, impl_ty={:?}", + let predicates = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_args) + .predicates + .into_iter() + .chain(Some(ty::Binder::dummy(impl_trait_ref).to_predicate(tcx))); + for predicate in predicates { + let obligation = traits::Obligation::new( + tcx, + traits::ObligationCause::dummy(), param_env, - impl_trait_ref, - impl_ty + predicate, ); - let predicates = cx - .tcx - .predicates_of(impl_def_id) - .instantiate(cx.tcx, impl_args) - .predicates - .into_iter() - .chain(Some(ty::Binder::dummy(impl_trait_ref).to_predicate(infcx.tcx))); - for predicate in predicates { - debug!("testing predicate {predicate:?}"); - let obligation = traits::Obligation::new( - infcx.tcx, - traits::ObligationCause::dummy(), - param_env, - predicate, - ); - match infcx.evaluate_obligation(&obligation) { - Ok(eval_result) if eval_result.may_apply() => {} - Err(traits::OverflowError::Canonical) => {} - _ => continue 'blanket_impls, - } + match infcx.evaluate_obligation(&obligation) { + Ok(eval_result) if eval_result.may_apply() => {} + Err(traits::OverflowError::Canonical) => {} + _ => continue 'blanket_impls, } - debug!( - "get_blanket_impls: found applicable impl for trait_ref={:?}, ty={:?}", - trait_ref, ty - ); + } + debug!("found applicable impl for trait ref {trait_ref:?}"); - cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id)); + cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id)); - impls.push(Item { - name: None, - attrs: Default::default(), - item_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, - kind: Box::new(ImplItem(Box::new(Impl { - unsafety: hir::Unsafety::Normal, - generics: clean_ty_generics( - cx, - cx.tcx.generics_of(impl_def_id), - cx.tcx.explicit_predicates_of(impl_def_id), - ), - // FIXME(eddyb) compute both `trait_` and `for_` from - // the post-inference `trait_ref`, as it's more accurate. - trait_: Some(clean_trait_ref_with_bindings( - cx, - ty::Binder::dummy(trait_ref.instantiate_identity()), - ThinVec::new(), - )), - for_: clean_middle_ty( - ty::Binder::dummy(ty.instantiate_identity()), - cx, - None, - None, - ), - items: cx - .tcx - .associated_items(impl_def_id) - .in_definition_order() - .filter(|item| !item.is_impl_trait_in_trait()) - .map(|item| clean_middle_assoc_item(item, cx)) - .collect::>(), - polarity: ty::ImplPolarity::Positive, - kind: ImplKind::Blanket(Box::new(clean_middle_ty( - ty::Binder::dummy(trait_ref.instantiate_identity().self_ty()), - cx, - None, - None, - ))), - }))), - cfg: None, - inline_stmt_id: None, - }); - } + blanket_impls.push(clean::Item { + name: None, + attrs: Default::default(), + item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, + kind: Box::new(clean::ImplItem(Box::new(clean::Impl { + unsafety: hir::Unsafety::Normal, + generics: clean_ty_generics( + cx, + tcx.generics_of(impl_def_id), + tcx.explicit_predicates_of(impl_def_id), + ), + // FIXME(eddyb) compute both `trait_` and `for_` from + // the post-inference `trait_ref`, as it's more accurate. + trait_: Some(clean_trait_ref_with_bindings( + cx, + ty::Binder::dummy(trait_ref.instantiate_identity()), + ThinVec::new(), + )), + for_: clean_middle_ty( + ty::Binder::dummy(ty.instantiate_identity()), + cx, + None, + None, + ), + items: tcx + .associated_items(impl_def_id) + .in_definition_order() + .filter(|item| !item.is_impl_trait_in_trait()) + .map(|item| clean_middle_assoc_item(item, cx)) + .collect(), + polarity: ty::ImplPolarity::Positive, + kind: clean::ImplKind::Blanket(Box::new(clean_middle_ty( + ty::Binder::dummy(trait_ref.instantiate_identity().self_ty()), + cx, + None, + None, + ))), + }))), + cfg: None, + inline_stmt_id: None, + }); } - - impls } + + blanket_impls } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index bbdfbbd0852c6..12f45fe497972 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -29,7 +29,7 @@ use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{self, ExpnKind}; +use rustc_span::ExpnKind; use rustc_trait_selection::traits::wf::object_region_bounds; use std::borrow::Cow; @@ -37,14 +37,14 @@ use std::collections::BTreeMap; use std::mem; use thin_vec::ThinVec; -use crate::core::{self, DocContext}; +use crate::core::DocContext; use crate::formats::item_type::ItemType; use crate::visit_ast::Module as DocModule; use utils::*; pub(crate) use self::types::*; -pub(crate) use self::utils::{get_auto_trait_and_blanket_impls, krate, register_res}; +pub(crate) use self::utils::{krate, register_res, synthesize_auto_trait_and_blanket_impls}; pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<'tcx>) -> Item { let mut items: Vec = vec![]; diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index d5e0e83696f3f..dc62fbb5edb55 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -1,5 +1,5 @@ use crate::clean::auto_trait::synthesize_auto_trait_impls; -use crate::clean::blanket_impl::BlanketImplFinder; +use crate::clean::blanket_impl::synthesize_blanket_impls; use crate::clean::render_macro_matchers::render_macro_matcher; use crate::clean::{ clean_doc_module, clean_middle_const, clean_middle_region, clean_middle_ty, inline, Crate, @@ -477,8 +477,7 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { } } -// FIXME(fmease): Update the `get_*` terminology to the `synthesize_` one. -pub(crate) fn get_auto_trait_and_blanket_impls( +pub(crate) fn synthesize_auto_trait_and_blanket_impls( cx: &mut DocContext<'_>, item_def_id: DefId, ) -> impl Iterator { @@ -490,8 +489,8 @@ pub(crate) fn get_auto_trait_and_blanket_impls( let blanket_impls = cx .sess() .prof - .generic_activity("get_blanket_impls") - .run(|| BlanketImplFinder { cx }.get_blanket_impls(item_def_id)); + .generic_activity("synthesize_blanket_impls") + .run(|| synthesize_blanket_impls(cx, item_def_id)); auto_impls.into_iter().chain(blanket_impls) } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index fbbb6152cfe79..c92cf9d3e80d3 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -122,7 +122,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> _ => true, } }) { - let impls = get_auto_trait_and_blanket_impls(cx, def_id); + let impls = synthesize_auto_trait_and_blanket_impls(cx, def_id); new_items_external.extend(impls.filter(|i| cx.inlined.insert(i.item_id))); } } @@ -230,8 +230,10 @@ impl<'a, 'tcx> DocVisitor for SyntheticImplCollector<'a, 'tcx> { if i.is_struct() || i.is_enum() || i.is_union() { // FIXME(eddyb) is this `doc(hidden)` check needed? if !self.cx.tcx.is_doc_hidden(i.item_id.expect_def_id()) { - self.impls - .extend(get_auto_trait_and_blanket_impls(self.cx, i.item_id.expect_def_id())); + self.impls.extend(synthesize_auto_trait_and_blanket_impls( + self.cx, + i.item_id.expect_def_id(), + )); } } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index bb8509fe41377..770496289e2e7 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2354,6 +2354,11 @@ impl<'test> TestCx<'test> { "ignore-directory-in-diagnostics-source-blocks={}", home::cargo_home().expect("failed to find cargo home").to_str().unwrap() )); + // Similarly, vendored sources shouldn't be shown when running from a dist tarball. + rustc.arg("-Z").arg(format!( + "ignore-directory-in-diagnostics-source-blocks={}", + self.config.find_rust_src_root().unwrap().join("vendor").display(), + )); // Optionally prevent default --sysroot if specified in test compile-flags. if !self.props.compile_flags.iter().any(|flag| flag.starts_with("--sysroot")) diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs index 6fc65e56901a7..39f7e70b69393 100644 --- a/src/tools/tidy/src/error_codes.rs +++ b/src/tools/tidy/src/error_codes.rs @@ -71,10 +71,12 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec) -> Vec) -> Vec .grey", {"color": |grey|}) - // Leave the search results to prevent reloading with an already filled search input. - press-key: "Escape" }, ) diff --git a/tests/rustdoc-gui/settings.goml b/tests/rustdoc-gui/settings.goml index 56d0f8624e8f6..0011e44ca59ea 100644 --- a/tests/rustdoc-gui/settings.goml +++ b/tests/rustdoc-gui/settings.goml @@ -36,7 +36,12 @@ wait-for: "#alternative-display #search" assert: "#main-content.hidden" // Now let's check the content of the settings menu. -call-function: ("switch-theme", {"theme": "dark"}) +// If we are on the settings page, the menu doesn't work the same so we set +// the theme manually. +set-local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"} +// We reload the page so the local storage settings are being used. +reload: + click: "#settings-menu" wait-for: "#settings" diff --git a/tests/rustdoc-gui/utils.goml b/tests/rustdoc-gui/utils.goml index d9f8726ec5318..844dc98a5374c 100644 --- a/tests/rustdoc-gui/utils.goml +++ b/tests/rustdoc-gui/utils.goml @@ -4,8 +4,15 @@ define-function: ( [theme], block { // Set the theme. - set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} - // We reload the page so the local storage settings are being used. - reload: + // Open the settings menu. + click: "#settings-menu" + // Wait for the popover to appear... + wait-for: "#settings" + // Change the setting. + click: "#theme-"+ |theme| + // Close the popover. + click: "#settings-menu" + // Ensure that the local storage was correctly updated. + assert-local-storage: {"rustdoc-theme": |theme|} }, ) diff --git a/tests/ui-fulldeps/stable-mir/check_binop.rs b/tests/ui-fulldeps/stable-mir/check_binop.rs new file mode 100644 index 0000000000000..3b52d88de3cdf --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_binop.rs @@ -0,0 +1,147 @@ +//@ run-pass +//! Test information regarding binary operations. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 + +#![feature(rustc_private)] + +extern crate rustc_hir; +#[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::mir::mono::Instance; +use stable_mir::mir::visit::{Location, MirVisitor}; +use stable_mir::mir::{LocalDecl, Rvalue, Statement, StatementKind, Terminator, TerminatorKind}; +use stable_mir::ty::{RigidTy, TyKind}; +use std::collections::HashSet; +use std::convert::TryFrom; +use std::io::Write; +use std::ops::ControlFlow; + +/// This function tests that we can correctly get type information from binary operations. +fn test_binops() -> ControlFlow<()> { + // Find items in the local crate. + let items = stable_mir::all_local_items(); + let mut instances = + items.into_iter().map(|item| Instance::try_from(item).unwrap()).collect::>(); + while let Some(instance) = instances.pop() { + // The test below shouldn't have recursion in it. + let Some(body) = instance.body() else { + continue; + }; + let mut visitor = Visitor { locals: body.locals(), calls: Default::default() }; + visitor.visit_body(&body); + instances.extend(visitor.calls.into_iter()); + } + ControlFlow::Continue(()) +} + +struct Visitor<'a> { + locals: &'a [LocalDecl], + calls: HashSet, +} + +impl<'a> MirVisitor for Visitor<'a> { + fn visit_statement(&mut self, stmt: &Statement, _loc: Location) { + match &stmt.kind { + StatementKind::Assign(place, Rvalue::BinaryOp(op, rhs, lhs)) => { + let ret_ty = place.ty(self.locals).unwrap(); + let op_ty = op.ty(rhs.ty(self.locals).unwrap(), lhs.ty(self.locals).unwrap()); + assert_eq!(ret_ty, op_ty, "Operation type should match the assigned place type"); + } + _ => {} + } + } + + fn visit_terminator(&mut self, term: &Terminator, _loc: Location) { + match &term.kind { + TerminatorKind::Call { func, .. } => { + let TyKind::RigidTy(RigidTy::FnDef(def, args)) = + func.ty(self.locals).unwrap().kind() + else { + return; + }; + self.calls.insert(Instance::resolve(def, &args).unwrap()); + } + _ => {} + } + } +} + +/// 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 = "binop_input.rs"; + generate_input(&path).unwrap(); + let args = vec!["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; + run!(args, test_binops).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + macro_rules! binop_int {{ + ($fn:ident, $typ:ty) => {{ + pub fn $fn(lhs: $typ, rhs: $typ) {{ + let eq = lhs == rhs; + let lt = lhs < rhs; + let le = lhs <= rhs; + + let sum = lhs + rhs; + let mult = lhs * sum; + let shift = mult << 2; + let bit_or = shift | rhs; + let cmp = lhs.cmp(&bit_or); + + // Try to avoid the results above being pruned + std::hint::black_box(((eq, lt, le), cmp)); + }} + }} + }} + + binop_int!(binop_u8, u8); + binop_int!(binop_i64, i64); + + pub fn binop_bool(lhs: bool, rhs: bool) {{ + let eq = lhs == rhs; + let or = lhs | eq; + let lt = lhs < or; + let cmp = lhs.cmp(&rhs); + + // Try to avoid the results above being pruned + std::hint::black_box((lt, cmp)); + }} + + pub fn binop_char(lhs: char, rhs: char) {{ + let eq = lhs == rhs; + let lt = lhs < rhs; + let cmp = lhs.cmp(&rhs); + + // Try to avoid the results above being pruned + std::hint::black_box(([eq, lt], cmp)); + }} + + pub fn binop_ptr(lhs: *const char, rhs: *const char) {{ + let eq = lhs == rhs; + let lt = lhs < rhs; + let cmp = lhs.cmp(&rhs); + let off = unsafe {{ lhs.offset(2) }}; + + // Try to avoid the results above being pruned + std::hint::black_box(([eq, lt], cmp, off)); + }} + "# + )?; + Ok(()) +} diff --git a/tests/ui/editions/edition-raw-pointer-method-2018.rs b/tests/ui/editions/edition-raw-pointer-method-2018.rs index b346953e187ee..ad47553d5b7a2 100644 --- a/tests/ui/editions/edition-raw-pointer-method-2018.rs +++ b/tests/ui/editions/edition-raw-pointer-method-2018.rs @@ -6,6 +6,6 @@ fn main() { let x = 0; let y = &x as *const _; + //~^ error: type annotations needed let _ = y.is_null(); - //~^ error: cannot call a method on a raw pointer with an unknown pointee type [E0699] } diff --git a/tests/ui/editions/edition-raw-pointer-method-2018.stderr b/tests/ui/editions/edition-raw-pointer-method-2018.stderr index 663843ad7bc78..2792d1e740056 100644 --- a/tests/ui/editions/edition-raw-pointer-method-2018.stderr +++ b/tests/ui/editions/edition-raw-pointer-method-2018.stderr @@ -1,9 +1,17 @@ -error[E0699]: cannot call a method on a raw pointer with an unknown pointee type - --> $DIR/edition-raw-pointer-method-2018.rs:9:15 +error[E0282]: type annotations needed for `*const _` + --> $DIR/edition-raw-pointer-method-2018.rs:8:9 | +LL | let y = &x as *const _; + | ^ +LL | LL | let _ = y.is_null(); - | ^^^^^^^ + | ------- cannot call a method on a raw pointer with an unknown pointee type + | +help: consider giving `y` an explicit type, where the placeholders `_` are specified + | +LL | let y: *const _ = &x as *const _; + | ++++++++++ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0699`. +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/issues/issue-21763.rs b/tests/ui/issues/issue-21763.rs index a349253063c02..1d0a0705cbbd9 100644 --- a/tests/ui/issues/issue-21763.rs +++ b/tests/ui/issues/issue-21763.rs @@ -1,6 +1,6 @@ // Regression test for HashMap only impl'ing Send/Sync if its contents do -//@ normalize-stderr-test: "\S+hashbrown-\S+" -> "$$HASHBROWN_SRC_LOCATION" +//@ normalize-stderr-test: "\S+[\\/]hashbrown\S+" -> "$$HASHBROWN_SRC_LOCATION" use std::collections::HashMap; use std::rc::Rc; diff --git a/tests/ui/methods/call_method_unknown_pointee.rs b/tests/ui/methods/call_method_unknown_pointee.rs index 1643b6bfa1813..a144e855ae3cb 100644 --- a/tests/ui/methods/call_method_unknown_pointee.rs +++ b/tests/ui/methods/call_method_unknown_pointee.rs @@ -8,10 +8,10 @@ fn main() { let ptr = &val as *const u32; unsafe { let _a: i32 = (ptr as *const _).read(); - //~^ ERROR cannot call a method on a raw pointer with an unknown pointee type [E0699] + //~^ ERROR type annotations needed let b = ptr as *const _; + //~^ ERROR type annotations needed let _b: u8 = b.read(); - //~^ ERROR cannot call a method on a raw pointer with an unknown pointee type [E0699] let _c = (ptr as *const u8).read(); // we know the type here } @@ -19,10 +19,10 @@ fn main() { let ptr = &mut val as *mut u32; unsafe { let _a: i32 = (ptr as *mut _).read(); - //~^ ERROR cannot call a method on a raw pointer with an unknown pointee type [E0699] + //~^ ERROR type annotations needed let b = ptr as *mut _; + //~^ ERROR type annotations needed b.write(10); - //~^ ERROR cannot call a method on a raw pointer with an unknown pointee type [E0699] (ptr as *mut i32).write(1000); // we know the type here } } diff --git a/tests/ui/methods/call_method_unknown_pointee.stderr b/tests/ui/methods/call_method_unknown_pointee.stderr index 84ecf046e7ac2..9d0f38cf6b5c1 100644 --- a/tests/ui/methods/call_method_unknown_pointee.stderr +++ b/tests/ui/methods/call_method_unknown_pointee.stderr @@ -1,27 +1,49 @@ -error[E0699]: cannot call a method on a raw pointer with an unknown pointee type +error[E0282]: type annotations needed --> $DIR/call_method_unknown_pointee.rs:10:41 | LL | let _a: i32 = (ptr as *const _).read(); | ^^^^ + | | + | cannot infer type + | cannot call a method on a raw pointer with an unknown pointee type -error[E0699]: cannot call a method on a raw pointer with an unknown pointee type - --> $DIR/call_method_unknown_pointee.rs:13:24 +error[E0282]: type annotations needed for `*const _` + --> $DIR/call_method_unknown_pointee.rs:12:13 | +LL | let b = ptr as *const _; + | ^ +LL | LL | let _b: u8 = b.read(); - | ^^^^ + | ---- cannot call a method on a raw pointer with an unknown pointee type + | +help: consider giving `b` an explicit type, where the placeholders `_` are specified + | +LL | let b: *const _ = ptr as *const _; + | ++++++++++ -error[E0699]: cannot call a method on a raw pointer with an unknown pointee type +error[E0282]: type annotations needed --> $DIR/call_method_unknown_pointee.rs:21:39 | LL | let _a: i32 = (ptr as *mut _).read(); | ^^^^ + | | + | cannot infer type + | cannot call a method on a raw pointer with an unknown pointee type -error[E0699]: cannot call a method on a raw pointer with an unknown pointee type - --> $DIR/call_method_unknown_pointee.rs:24:11 +error[E0282]: type annotations needed for `*mut _` + --> $DIR/call_method_unknown_pointee.rs:23:13 | +LL | let b = ptr as *mut _; + | ^ +LL | LL | b.write(10); - | ^^^^^ + | ----- cannot call a method on a raw pointer with an unknown pointee type + | +help: consider giving `b` an explicit type, where the placeholders `_` are specified + | +LL | let b: *mut _ = ptr as *mut _; + | ++++++++ error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0699`. +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/sanitizer/kcfi-mangling.rs b/tests/ui/sanitizer/kcfi-mangling.rs new file mode 100644 index 0000000000000..fde7b5451b6e2 --- /dev/null +++ b/tests/ui/sanitizer/kcfi-mangling.rs @@ -0,0 +1,30 @@ +// Check KCFI extra mangling works correctly on v0 + +//@ needs-sanitizer-kcfi +//@ no-prefer-dynamic +//@ compile-flags: -C panic=abort -Zsanitizer=kcfi -C symbol-mangling-version=v0 +//@ build-pass + +trait Foo { + fn foo(&self); +} + +struct Bar; +impl Foo for Bar { + fn foo(&self) {} +} + +struct Baz; +impl Foo for Baz { + #[track_caller] + fn foo(&self) {} +} + +fn main() { + // Produces `ReifyShim(_, ReifyReason::FnPtr)` + let f: fn(&Bar) = Bar::foo; + f(&Bar); + // Produces `ReifyShim(_, ReifyReason::Vtable)` + let v: &dyn Foo = &Baz as _; + v.foo(); +} diff --git a/tests/ui/type/pattern_types/unimplemented_pat.rs b/tests/ui/type/pattern_types/unimplemented_pat.rs new file mode 100644 index 0000000000000..c02160ff58a6c --- /dev/null +++ b/tests/ui/type/pattern_types/unimplemented_pat.rs @@ -0,0 +1,15 @@ +//! This test ensures we do not ICE for unimplemented +//! patterns unless the feature gate is enabled. + +#![feature(core_pattern_type)] +#![feature(core_pattern_types)] + +use std::pat::pattern_type; + +type Always = pattern_type!(Option is Some(_)); +//~^ ERROR: pattern types are unstable + +type Binding = pattern_type!(Option is x); +//~^ ERROR: pattern types are unstable + +fn main() {} diff --git a/tests/ui/type/pattern_types/unimplemented_pat.stderr b/tests/ui/type/pattern_types/unimplemented_pat.stderr new file mode 100644 index 0000000000000..481c6017dccc2 --- /dev/null +++ b/tests/ui/type/pattern_types/unimplemented_pat.stderr @@ -0,0 +1,23 @@ +error[E0658]: pattern types are unstable + --> $DIR/unimplemented_pat.rs:9:15 + | +LL | type Always = pattern_type!(Option is Some(_)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #123646 for more information + = help: add `#![feature(pattern_types)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pattern types are unstable + --> $DIR/unimplemented_pat.rs:12:16 + | +LL | type Binding = pattern_type!(Option is x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #123646 for more information + = help: add `#![feature(pattern_types)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`.