Auto merge of #123663 - matthiaskrgr:rollup-1qnj9j3, r=matthiaskrgr
Rollup of 9 pull requests Successful merges: - #122768 (Use the more informative generic type inference failure error on method calls on raw pointers) - #123620 (sanitizers: Create the rustc_sanitizers crate) - #123624 ([rustdoc] [GUI tests] Make theme switching closer to reality) - #123636 (Update books) - #123647 (rustdoc: slightly clean up the synthesis of blanket impls) - #123648 (Avoid ICEing without the pattern_types feature gate) - #123649 (KCFI: Use legal charset in shim encoding) - #123652 (Fix UI tests with dist-vendored dependencies) - #123655 (Remove unimplemented!() from BinOp::ty() function) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
86b603cd79
58 changed files with 1481 additions and 1066 deletions
19
Cargo.lock
19
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]]
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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)]))
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"),
|
||||
};
|
||||
|
|
15
compiler/rustc_sanitizers/Cargo.toml
Normal file
15
compiler/rustc_sanitizers/Cargo.toml
Normal file
|
@ -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" }
|
2
compiler/rustc_sanitizers/README.md
Normal file
2
compiler/rustc_sanitizers/README.md
Normal file
|
@ -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.
|
6
compiler/rustc_sanitizers/src/cfi/mod.rs
Normal file
6
compiler/rustc_sanitizers/src/cfi/mod.rs
Normal file
|
@ -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};
|
|
@ -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
|
||||
/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>).
|
||||
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
|
||||
/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>).
|
||||
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<DictKey<'tcx>, usize>,
|
||||
options: EncodeTyOptions,
|
||||
) -> String {
|
||||
// [I<subst1..substN>E] as part of vendor extended type
|
||||
let mut s = String::new();
|
||||
let args: Vec<GenericArg<'_>> = 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
|
||||
/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
|
||||
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<DictKey<'tcx>,
|
|||
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<DictKey<'tcx>, usize>,
|
||||
options: EncodeTyOptions,
|
||||
) -> String {
|
||||
// [I<subst1..substN>E] as part of vendor extended type
|
||||
let mut s = String::new();
|
||||
let args: Vec<GenericArg<'_>> = 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 <name> for use in u<length><name>[I<element-type1..element-typeN>E], where
|
||||
// <element-type> is <subst>, using v0's <path> without v0's extended form of paths:
|
||||
//
|
||||
// N<namespace-tagN>..N<namespace-tag1>
|
||||
// C<crate-disambiguator><crate-name>
|
||||
// <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN>
|
||||
//
|
||||
// With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance:
|
||||
//
|
||||
// pub type Type1 = impl Send;
|
||||
// let _: Type1 = <Struct1<i32>>::foo;
|
||||
// fn foo1(_: Type1) { }
|
||||
//
|
||||
// pub type Type2 = impl Send;
|
||||
// let _: Type2 = <Trait1<i32>>::foo;
|
||||
// fn foo2(_: Type2) { }
|
||||
//
|
||||
// pub type Type3 = impl Send;
|
||||
// let _: Type3 = <i32 as Trait1<i32>>::foo;
|
||||
// fn foo3(_: Type3) { }
|
||||
//
|
||||
// pub type Type4 = impl Send;
|
||||
// let _: Type4 = <Struct1<i32> as Trait1<i32>>::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 <namespace>
|
||||
hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace>
|
||||
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<DictKey<'tcx>, usize>,
|
||||
|
@ -762,486 +635,119 @@ fn encode_ty<'tcx>(
|
|||
typeid
|
||||
}
|
||||
|
||||
struct TransformTy<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
options: TransformTyOptions,
|
||||
parents: Vec<Ty<'tcx>>,
|
||||
}
|
||||
/// Encodes a ty:Ty name, including its crate and path disambiguators and names.
|
||||
fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
|
||||
// Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where
|
||||
// <element-type> is <subst>, using v0's <path> without v0's extended form of paths:
|
||||
//
|
||||
// N<namespace-tagN>..N<namespace-tag1>
|
||||
// C<crate-disambiguator><crate-name>
|
||||
// <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN>
|
||||
//
|
||||
// With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance:
|
||||
//
|
||||
// pub type Type1 = impl Send;
|
||||
// let _: Type1 = <Struct1<i32>>::foo;
|
||||
// fn foo1(_: Type1) { }
|
||||
//
|
||||
// pub type Type2 = impl Send;
|
||||
// let _: Type2 = <Trait1<i32>>::foo;
|
||||
// fn foo2(_: Type2) { }
|
||||
//
|
||||
// pub type Type3 = impl Send;
|
||||
// let _: Type3 = <i32 as Trait1<i32>>::foo;
|
||||
// fn foo3(_: Type3) { }
|
||||
//
|
||||
// pub type Type4 = impl Send;
|
||||
// let _: Type4 = <Struct1<i32> as Trait1<i32>>::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();
|
||||
|
||||
impl<'tcx> TransformTy<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self {
|
||||
TransformTy { tcx, options, parents: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFolder<TyCtxt<'tcx>> 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
|
||||
}
|
||||
// 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 <namespace>
|
||||
hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace>
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<DictKey<'tcx>, 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.
|
||||
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));
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
// 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);
|
||||
}
|
||||
|
||||
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)
|
||||
s
|
||||
}
|
||||
|
||||
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 disambiguator (see
|
||||
/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>).
|
||||
fn to_disambiguator(num: u64) -> String {
|
||||
if let Some(num) = num.checked_sub(1) {
|
||||
format!("s{}_", base_n::encode(num as u128, 62))
|
||||
} 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
|
||||
"s_".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)
|
||||
/// Converts a number to a sequence number (see
|
||||
/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>).
|
||||
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()
|
||||
}
|
||||
}
|
123
compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs
Normal file
123
compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs
Normal file
|
@ -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<DictKey<'tcx>, 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)
|
||||
}
|
|
@ -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<Ty<'tcx>>,
|
||||
}
|
||||
|
||||
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<TyCtxt<'tcx>> 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
|
||||
}
|
54
compiler/rustc_sanitizers/src/cfi/typeid/mod.rs
Normal file
54
compiler/rustc_sanitizers/src/cfi/typeid/mod.rs
Normal file
|
@ -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)
|
||||
}
|
7
compiler/rustc_sanitizers/src/kcfi/mod.rs
Normal file
7
compiler/rustc_sanitizers/src/kcfi/mod.rs
Normal file
|
@ -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};
|
55
compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs
Normal file
55
compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs
Normal file
|
@ -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
|
||||
}
|
7
compiler/rustc_sanitizers/src/lib.rs
Normal file
7
compiler/rustc_sanitizers/src/lib.rs
Normal file
|
@ -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;
|
|
@ -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<T> RustcInternal for &T
|
||||
where
|
||||
T: RustcInternal,
|
||||
|
|
|
@ -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<Tables<'tcx>>);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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"),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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'"
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.17.1
|
||||
0.17.2
|
|
@ -1 +1 @@
|
|||
Subproject commit 19c40bfd2d57641d962f3119a1c343355f1b3c5e
|
||||
Subproject commit 3131aa4642c627a24f523c82566b94a7d920f68c
|
|
@ -1 +1 @@
|
|||
Subproject commit 98b33e9a441457b0a491fe1be90e7de64eafc3e5
|
||||
Subproject commit eb3eb80e106d03250c1fb7c5666b1c8c59672862
|
|
@ -1 +1 @@
|
|||
Subproject commit 2e95fc2fd31d669947e993aa07ef10dc9828bee7
|
||||
Subproject commit aa7d4b0b4653ddb47cb1de2036d090ec2ba9dab1
|
|
@ -1 +1 @@
|
|||
Subproject commit 6bc2415218d4dd0cb01433d8320f5ccf79c343a1
|
||||
Subproject commit 0d5f88475fe285affa6dbbc806e9e44d730797c0
|
|
@ -1 +1 @@
|
|||
Subproject commit 984b36eca4b9293df04d5ba4eb5c4f77db0f51dc
|
||||
Subproject commit 55694913b1301cc809f9bf4a1ad1b3d6920efbd9
|
|
@ -1 +1 @@
|
|||
Subproject commit 7601e0c5ad29d5bd3b518700ea63fddfff5915a7
|
||||
Subproject commit 60d34b5fd33db1346f9aabfc0c9d0bda6c8e42be
|
|
@ -1 +1 @@
|
|||
Subproject commit ffa246b7fd95a96e1cd54883e613aed42c32547d
|
||||
Subproject commit b77a34bd46399687b4ce6a17198e9f316c988794
|
|
@ -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;
|
||||
|
||||
impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
|
||||
pub(crate) fn get_blanket_impls(&mut self, item_def_id: DefId) -> Vec<Item> {
|
||||
let cx = &mut self.cx;
|
||||
let ty = cx.tcx.type_of(item_def_id);
|
||||
#[instrument(level = "debug", skip(cx))]
|
||||
pub(crate) fn synthesize_blanket_impls(
|
||||
cx: &mut DocContext<'_>,
|
||||
item_def_id: DefId,
|
||||
) -> Vec<clean::Item> {
|
||||
let tcx = cx.tcx;
|
||||
let ty = tcx.type_of(item_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 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:?}`");
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
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::<Vec<_>>(),
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
debug!("found applicable impl for trait ref {trait_ref:?}");
|
||||
|
||||
impls
|
||||
cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id));
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
blanket_impls
|
||||
}
|
||||
|
|
|
@ -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<Item> = vec![];
|
||||
|
|
|
@ -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<Item = Item> {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -71,10 +71,12 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String
|
|||
let path = root_path.join(Path::new(ERROR_CODES_PATH));
|
||||
let file =
|
||||
fs::read_to_string(&path).unwrap_or_else(|e| panic!("failed to read `{path:?}`: {e}"));
|
||||
let path = path.display();
|
||||
|
||||
let mut error_codes = Vec::new();
|
||||
|
||||
for line in file.lines() {
|
||||
for (line_index, line) in file.lines().enumerate() {
|
||||
let line_index = line_index + 1;
|
||||
let line = line.trim();
|
||||
|
||||
if line.starts_with('E') {
|
||||
|
@ -82,39 +84,54 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String
|
|||
|
||||
// Extract the error code from the line. Emit a fatal error if it is not in the correct
|
||||
// format.
|
||||
let err_code = if let Some(err_code) = split_line {
|
||||
err_code.0.to_owned()
|
||||
} else {
|
||||
let Some(split_line) = split_line else {
|
||||
errors.push(format!(
|
||||
"Expected a line with the format `Eabcd: abcd, \
|
||||
"{path}:{line_index}: Expected a line with the format `Eabcd: abcd, \
|
||||
but got \"{}\" without a `:` delimiter",
|
||||
line,
|
||||
));
|
||||
continue;
|
||||
};
|
||||
|
||||
let err_code = split_line.0.to_owned();
|
||||
|
||||
// If this is a duplicate of another error code, emit a fatal error.
|
||||
if error_codes.contains(&err_code) {
|
||||
errors.push(format!("Found duplicate error code: `{}`", err_code));
|
||||
errors.push(format!(
|
||||
"{path}:{line_index}: Found duplicate error code: `{}`",
|
||||
err_code
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut chars = err_code.chars();
|
||||
chars.next();
|
||||
assert_eq!(chars.next(), Some('E'));
|
||||
let error_num_as_str = chars.as_str();
|
||||
|
||||
// Ensure that the line references the correct markdown file.
|
||||
let expected_filename = format!(" {},", error_num_as_str);
|
||||
if expected_filename != split_line.unwrap().1 {
|
||||
let rest = split_line.1.split_once(',');
|
||||
let Some(rest) = rest else {
|
||||
errors.push(format!(
|
||||
"`{}:` should be followed by `{}` but instead found `{}` in \
|
||||
"{path}:{line_index}: Expected a line with the format `Eabcd: abcd, \
|
||||
but got \"{}\" without a `,` delimiter",
|
||||
line,
|
||||
));
|
||||
continue;
|
||||
};
|
||||
if error_num_as_str != rest.0.trim() {
|
||||
errors.push(format!(
|
||||
"{path}:{line_index}: `{}:` should be followed by `{},` but instead found `{}` in \
|
||||
`compiler/rustc_error_codes/src/lib.rs`",
|
||||
err_code,
|
||||
expected_filename,
|
||||
split_line.unwrap().1,
|
||||
error_num_as_str,
|
||||
split_line.1,
|
||||
));
|
||||
continue;
|
||||
}
|
||||
if !rest.1.trim().is_empty() && !rest.1.trim().starts_with("//") {
|
||||
errors.push(format!("{path}:{line_index}: should only have one error per line"));
|
||||
continue;
|
||||
}
|
||||
|
||||
error_codes.push(err_code);
|
||||
}
|
||||
|
@ -146,14 +163,14 @@ fn check_error_codes_docs(
|
|||
return;
|
||||
}
|
||||
|
||||
// Make sure that the file is referenced in `error_codes.rs`
|
||||
// Make sure that the file is referenced in `rustc_error_codes/src/lib.rs`
|
||||
let filename = path.file_name().unwrap().to_str().unwrap().split_once('.');
|
||||
let err_code = filename.unwrap().0; // `unwrap` is ok because we know the filename is in the correct format.
|
||||
|
||||
if error_codes.iter().all(|e| e != err_code) {
|
||||
errors.push(format!(
|
||||
"Found valid file `{}` in error code docs directory without corresponding \
|
||||
entry in `error_code.rs`",
|
||||
entry in `rustc_error_codes/src/lib.rs`",
|
||||
path.display()
|
||||
));
|
||||
return;
|
||||
|
|
|
@ -2,23 +2,25 @@
|
|||
include: "utils.goml"
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
|
||||
|
||||
// Otherwise, we can't check text color
|
||||
show-text: true
|
||||
|
||||
// We check that without this setting, there is no line number displayed.
|
||||
assert-false: "pre.example-line-numbers"
|
||||
|
||||
// We set the setting to show the line numbers on code examples.
|
||||
set-local-storage: {"rustdoc-line-numbers": "true"}
|
||||
reload:
|
||||
// We wait for the line numbers to be added into the DOM by the JS...
|
||||
wait-for: "pre.example-line-numbers"
|
||||
|
||||
// Otherwise, we can't check text color
|
||||
show-text: true
|
||||
|
||||
// Let's now check some CSS properties...
|
||||
define-function: (
|
||||
"check-colors",
|
||||
[theme, color],
|
||||
block {
|
||||
// We now set the setting to show the line numbers on code examples.
|
||||
set-local-storage: {"rustdoc-line-numbers": "true"}
|
||||
// Page will be reloaded in "switch-theme".
|
||||
call-function: ("switch-theme", {"theme": |theme|})
|
||||
// We wait for the line numbers to be added into the DOM by the JS...
|
||||
wait-for: "pre.example-line-numbers"
|
||||
// If the test didn't fail, it means that it was found!
|
||||
assert-css: (
|
||||
"pre.example-line-numbers",
|
||||
|
|
|
@ -9,6 +9,7 @@ define-function: (
|
|||
[theme, toggle_line_color, toggle_line_hover_color],
|
||||
block {
|
||||
call-function: ("switch-theme", {"theme": |theme|})
|
||||
reload:
|
||||
|
||||
// Clicking "More examples..." will open additional examples
|
||||
assert-attribute-false: (".more-examples-toggle", {"open": ""})
|
||||
|
@ -21,6 +22,8 @@ define-function: (
|
|||
".toggle-line:hover .toggle-line-inner",
|
||||
{"background-color": |toggle_line_hover_color|},
|
||||
)
|
||||
// We put the toggle in the original state.
|
||||
click: ".more-examples-toggle"
|
||||
// Moving cursor away from the toggle line to prevent disrupting next test.
|
||||
move-cursor-to: ".search-input"
|
||||
},
|
||||
|
|
|
@ -210,24 +210,21 @@ call-function: ("check-search-color", {
|
|||
|
||||
// Check the alias.
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
// If the text isn't displayed, the browser doesn't compute color style correctly...
|
||||
show-text: true
|
||||
|
||||
write-into: (".search-input", "thisisanalias")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
|
||||
define-function: (
|
||||
"check-alias",
|
||||
[theme, alias, grey],
|
||||
block {
|
||||
call-function: ("switch-theme", {"theme": |theme|})
|
||||
write-into: (".search-input", "thisisanalias")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
// Checking that the colors for the alias element are the ones expected.
|
||||
assert-css: (".result-name .path .alias", {"color": |alias|})
|
||||
assert-css: (".result-name .path .alias > .grey", {"color": |grey|})
|
||||
// Leave the search results to prevent reloading with an already filled search input.
|
||||
press-key: "Escape"
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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|}
|
||||
},
|
||||
)
|
||||
|
|
147
tests/ui-fulldeps/stable-mir/check_binop.rs
Normal file
147
tests/ui-fulldeps/stable-mir/check_binop.rs
Normal file
|
@ -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::<Vec<_>>();
|
||||
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<Instance>,
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`.
|
||||
|
|
30
tests/ui/sanitizer/kcfi-mangling.rs
Normal file
30
tests/ui/sanitizer/kcfi-mangling.rs
Normal file
|
@ -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();
|
||||
}
|
15
tests/ui/type/pattern_types/unimplemented_pat.rs
Normal file
15
tests/ui/type/pattern_types/unimplemented_pat.rs
Normal file
|
@ -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<u32> is Some(_));
|
||||
//~^ ERROR: pattern types are unstable
|
||||
|
||||
type Binding = pattern_type!(Option<u32> is x);
|
||||
//~^ ERROR: pattern types are unstable
|
||||
|
||||
fn main() {}
|
23
tests/ui/type/pattern_types/unimplemented_pat.stderr
Normal file
23
tests/ui/type/pattern_types/unimplemented_pat.stderr
Normal file
|
@ -0,0 +1,23 @@
|
|||
error[E0658]: pattern types are unstable
|
||||
--> $DIR/unimplemented_pat.rs:9:15
|
||||
|
|
||||
LL | type Always = pattern_type!(Option<u32> is Some(_));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #123646 <https://github.com/rust-lang/rust/issues/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<u32> is x);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #123646 <https://github.com/rust-lang/rust/issues/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`.
|
Loading…
Add table
Add a link
Reference in a new issue