Merge from rustc
This commit is contained in:
commit
dc21c77ae6
130 changed files with 1117 additions and 476 deletions
21
Cargo.lock
21
Cargo.lock
|
|
@ -2022,9 +2022,9 @@ checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.171"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "libdbus-sys"
|
||||
|
|
@ -2572,16 +2572,6 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
|
|
@ -3142,7 +3132,6 @@ dependencies = [
|
|||
"gimli 0.31.1",
|
||||
"libc",
|
||||
"object 0.36.7",
|
||||
"os_pipe",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"similar",
|
||||
|
|
@ -3199,14 +3188,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc-rayon-core"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67668daaf00e359c126f6dcb40d652d89b458a008c8afa727a42a2d20fca0b7f"
|
||||
checksum = "2f42932dcd3bcbe484b38a3ccf79b7906fac41c02d408b5b1bac26da3416efdb"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -50,8 +50,16 @@ pub enum DiffActivity {
|
|||
/// with it.
|
||||
Dual,
|
||||
/// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument
|
||||
/// with it. It expects the shadow argument to be `width` times larger than the original
|
||||
/// input/output.
|
||||
Dualv,
|
||||
/// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument
|
||||
/// with it. Drop the code which updates the original input/output for maximum performance.
|
||||
DualOnly,
|
||||
/// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument
|
||||
/// with it. Drop the code which updates the original input/output for maximum performance.
|
||||
/// It expects the shadow argument to be `width` times larger than the original input/output.
|
||||
DualvOnly,
|
||||
/// Reverse Mode, Compute derivatives for this &T or *T input and *add* it to the shadow argument.
|
||||
Duplicated,
|
||||
/// Reverse Mode, Compute derivatives for this &T or *T input and *add* it to the shadow argument.
|
||||
|
|
@ -59,7 +67,15 @@ pub enum DiffActivity {
|
|||
DuplicatedOnly,
|
||||
/// All Integers must be Const, but these are used to mark the integer which represents the
|
||||
/// length of a slice/vec. This is used for safety checks on slices.
|
||||
FakeActivitySize,
|
||||
/// The integer (if given) specifies the size of the slice element in bytes.
|
||||
FakeActivitySize(Option<u32>),
|
||||
}
|
||||
|
||||
impl DiffActivity {
|
||||
pub fn is_dual_or_const(&self) -> bool {
|
||||
use DiffActivity::*;
|
||||
matches!(self, |Dual| DualOnly | Dualv | DualvOnly | Const)
|
||||
}
|
||||
}
|
||||
/// We generate one of these structs for each `#[autodiff(...)]` attribute.
|
||||
#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
|
|
@ -131,11 +147,7 @@ pub fn valid_ret_activity(mode: DiffMode, activity: DiffActivity) -> bool {
|
|||
match mode {
|
||||
DiffMode::Error => false,
|
||||
DiffMode::Source => false,
|
||||
DiffMode::Forward => {
|
||||
activity == DiffActivity::Dual
|
||||
|| activity == DiffActivity::DualOnly
|
||||
|| activity == DiffActivity::Const
|
||||
}
|
||||
DiffMode::Forward => activity.is_dual_or_const(),
|
||||
DiffMode::Reverse => {
|
||||
activity == DiffActivity::Const
|
||||
|| activity == DiffActivity::Active
|
||||
|
|
@ -153,10 +165,8 @@ pub fn valid_ret_activity(mode: DiffMode, activity: DiffActivity) -> bool {
|
|||
pub fn valid_ty_for_activity(ty: &P<Ty>, activity: DiffActivity) -> bool {
|
||||
use DiffActivity::*;
|
||||
// It's always allowed to mark something as Const, since we won't compute derivatives wrt. it.
|
||||
if matches!(activity, Const) {
|
||||
return true;
|
||||
}
|
||||
if matches!(activity, Dual | DualOnly) {
|
||||
// Dual variants also support all types.
|
||||
if activity.is_dual_or_const() {
|
||||
return true;
|
||||
}
|
||||
// FIXME(ZuseZ4) We should make this more robust to also
|
||||
|
|
@ -172,9 +182,7 @@ pub fn valid_input_activity(mode: DiffMode, activity: DiffActivity) -> bool {
|
|||
return match mode {
|
||||
DiffMode::Error => false,
|
||||
DiffMode::Source => false,
|
||||
DiffMode::Forward => {
|
||||
matches!(activity, Dual | DualOnly | Const)
|
||||
}
|
||||
DiffMode::Forward => activity.is_dual_or_const(),
|
||||
DiffMode::Reverse => {
|
||||
matches!(activity, Active | ActiveOnly | Duplicated | DuplicatedOnly | Const)
|
||||
}
|
||||
|
|
@ -189,10 +197,12 @@ impl Display for DiffActivity {
|
|||
DiffActivity::Active => write!(f, "Active"),
|
||||
DiffActivity::ActiveOnly => write!(f, "ActiveOnly"),
|
||||
DiffActivity::Dual => write!(f, "Dual"),
|
||||
DiffActivity::Dualv => write!(f, "Dualv"),
|
||||
DiffActivity::DualOnly => write!(f, "DualOnly"),
|
||||
DiffActivity::DualvOnly => write!(f, "DualvOnly"),
|
||||
DiffActivity::Duplicated => write!(f, "Duplicated"),
|
||||
DiffActivity::DuplicatedOnly => write!(f, "DuplicatedOnly"),
|
||||
DiffActivity::FakeActivitySize => write!(f, "FakeActivitySize"),
|
||||
DiffActivity::FakeActivitySize(s) => write!(f, "FakeActivitySize({:?})", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -220,7 +230,9 @@ impl FromStr for DiffActivity {
|
|||
"ActiveOnly" => Ok(DiffActivity::ActiveOnly),
|
||||
"Const" => Ok(DiffActivity::Const),
|
||||
"Dual" => Ok(DiffActivity::Dual),
|
||||
"Dualv" => Ok(DiffActivity::Dualv),
|
||||
"DualOnly" => Ok(DiffActivity::DualOnly),
|
||||
"DualvOnly" => Ok(DiffActivity::DualvOnly),
|
||||
"Duplicated" => Ok(DiffActivity::Duplicated),
|
||||
"DuplicatedOnly" => Ok(DiffActivity::DuplicatedOnly),
|
||||
_ => Err(()),
|
||||
|
|
|
|||
|
|
@ -799,8 +799,19 @@ mod llvm_enzyme {
|
|||
d_inputs.push(shadow_arg.clone());
|
||||
}
|
||||
}
|
||||
DiffActivity::Dual | DiffActivity::DualOnly => {
|
||||
for i in 0..x.width {
|
||||
DiffActivity::Dual
|
||||
| DiffActivity::DualOnly
|
||||
| DiffActivity::Dualv
|
||||
| DiffActivity::DualvOnly => {
|
||||
// the *v variants get lowered to enzyme_dupv and enzyme_dupnoneedv, which cause
|
||||
// Enzyme to not expect N arguments, but one argument (which is instead larger).
|
||||
let iterations =
|
||||
if matches!(activity, DiffActivity::Dualv | DiffActivity::DualvOnly) {
|
||||
1
|
||||
} else {
|
||||
x.width
|
||||
};
|
||||
for i in 0..iterations {
|
||||
let mut shadow_arg = arg.clone();
|
||||
let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
|
||||
ident.name
|
||||
|
|
@ -823,7 +834,7 @@ mod llvm_enzyme {
|
|||
DiffActivity::Const => {
|
||||
// Nothing to do here.
|
||||
}
|
||||
DiffActivity::None | DiffActivity::FakeActivitySize => {
|
||||
DiffActivity::None | DiffActivity::FakeActivitySize(_) => {
|
||||
panic!("Should not happen");
|
||||
}
|
||||
}
|
||||
|
|
@ -887,8 +898,8 @@ mod llvm_enzyme {
|
|||
}
|
||||
};
|
||||
|
||||
if let DiffActivity::Dual = x.ret_activity {
|
||||
let kind = if x.width == 1 {
|
||||
if matches!(x.ret_activity, DiffActivity::Dual | DiffActivity::Dualv) {
|
||||
let kind = if x.width == 1 || matches!(x.ret_activity, DiffActivity::Dualv) {
|
||||
// Dual can only be used for f32/f64 ret.
|
||||
// In that case we return now a tuple with two floats.
|
||||
TyKind::Tup(thin_vec![ty.clone(), ty.clone()])
|
||||
|
|
@ -903,7 +914,7 @@ mod llvm_enzyme {
|
|||
let ty = P(rustc_ast::Ty { kind, id: ty.id, span: ty.span, tokens: None });
|
||||
d_decl.output = FnRetTy::Ty(ty);
|
||||
}
|
||||
if let DiffActivity::DualOnly = x.ret_activity {
|
||||
if matches!(x.ret_activity, DiffActivity::DualOnly | DiffActivity::DualvOnly) {
|
||||
// No need to change the return type,
|
||||
// we will just return the shadow in place of the primal return.
|
||||
// However, if we have a width > 1, then we don't return -> T, but -> [T; width]
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
|
|||
/// Empty string, to be used where LLVM expects an instruction name, indicating
|
||||
/// that the instruction is to be left unnamed (i.e. numbered, in textual IR).
|
||||
// FIXME(eddyb) pass `&CStr` directly to FFI once it's a thin pointer.
|
||||
const UNNAMED: *const c_char = c"".as_ptr();
|
||||
pub(crate) const UNNAMED: *const c_char = c"".as_ptr();
|
||||
|
||||
impl<'ll, CX: Borrow<SCx<'ll>>> BackendTypes for GenericBuilder<'_, 'll, CX> {
|
||||
type Value = <GenericCx<'ll, CX> as BackendTypes>::Value;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use rustc_middle::bug;
|
|||
use tracing::{debug, trace};
|
||||
|
||||
use crate::back::write::llvm_err;
|
||||
use crate::builder::SBuilder;
|
||||
use crate::builder::{SBuilder, UNNAMED};
|
||||
use crate::context::SimpleCx;
|
||||
use crate::declare::declare_simple_fn;
|
||||
use crate::errors::{AutoDiffWithoutEnable, LlvmError};
|
||||
|
|
@ -51,6 +51,7 @@ fn has_sret(fnc: &Value) -> bool {
|
|||
// using iterators and peek()?
|
||||
fn match_args_from_caller_to_enzyme<'ll>(
|
||||
cx: &SimpleCx<'ll>,
|
||||
builder: &SBuilder<'ll, 'll>,
|
||||
width: u32,
|
||||
args: &mut Vec<&'ll llvm::Value>,
|
||||
inputs: &[DiffActivity],
|
||||
|
|
@ -78,7 +79,9 @@ fn match_args_from_caller_to_enzyme<'ll>(
|
|||
let enzyme_const = cx.create_metadata("enzyme_const".to_string()).unwrap();
|
||||
let enzyme_out = cx.create_metadata("enzyme_out".to_string()).unwrap();
|
||||
let enzyme_dup = cx.create_metadata("enzyme_dup".to_string()).unwrap();
|
||||
let enzyme_dupv = cx.create_metadata("enzyme_dupv".to_string()).unwrap();
|
||||
let enzyme_dupnoneed = cx.create_metadata("enzyme_dupnoneed".to_string()).unwrap();
|
||||
let enzyme_dupnoneedv = cx.create_metadata("enzyme_dupnoneedv".to_string()).unwrap();
|
||||
|
||||
while activity_pos < inputs.len() {
|
||||
let diff_activity = inputs[activity_pos as usize];
|
||||
|
|
@ -90,13 +93,34 @@ fn match_args_from_caller_to_enzyme<'ll>(
|
|||
DiffActivity::Active => (enzyme_out, false),
|
||||
DiffActivity::ActiveOnly => (enzyme_out, false),
|
||||
DiffActivity::Dual => (enzyme_dup, true),
|
||||
DiffActivity::Dualv => (enzyme_dupv, true),
|
||||
DiffActivity::DualOnly => (enzyme_dupnoneed, true),
|
||||
DiffActivity::DualvOnly => (enzyme_dupnoneedv, true),
|
||||
DiffActivity::Duplicated => (enzyme_dup, true),
|
||||
DiffActivity::DuplicatedOnly => (enzyme_dupnoneed, true),
|
||||
DiffActivity::FakeActivitySize => (enzyme_const, false),
|
||||
DiffActivity::FakeActivitySize(_) => (enzyme_const, false),
|
||||
};
|
||||
let outer_arg = outer_args[outer_pos];
|
||||
args.push(cx.get_metadata_value(activity));
|
||||
if matches!(diff_activity, DiffActivity::Dualv) {
|
||||
let next_outer_arg = outer_args[outer_pos + 1];
|
||||
let elem_bytes_size: u64 = match inputs[activity_pos + 1] {
|
||||
DiffActivity::FakeActivitySize(Some(s)) => s.into(),
|
||||
_ => bug!("incorrect Dualv handling recognized."),
|
||||
};
|
||||
// stride: sizeof(T) * n_elems.
|
||||
// n_elems is the next integer.
|
||||
// Now we multiply `4 * next_outer_arg` to get the stride.
|
||||
let mul = unsafe {
|
||||
llvm::LLVMBuildMul(
|
||||
builder.llbuilder,
|
||||
cx.get_const_i64(elem_bytes_size),
|
||||
next_outer_arg,
|
||||
UNNAMED,
|
||||
)
|
||||
};
|
||||
args.push(mul);
|
||||
}
|
||||
args.push(outer_arg);
|
||||
if duplicated {
|
||||
// We know that duplicated args by construction have a following argument,
|
||||
|
|
@ -114,7 +138,7 @@ fn match_args_from_caller_to_enzyme<'ll>(
|
|||
} else {
|
||||
let next_activity = inputs[activity_pos + 1];
|
||||
// We analyze the MIR types and add this dummy activity if we visit a slice.
|
||||
next_activity == DiffActivity::FakeActivitySize
|
||||
matches!(next_activity, DiffActivity::FakeActivitySize(_))
|
||||
}
|
||||
};
|
||||
if slice {
|
||||
|
|
@ -125,7 +149,10 @@ fn match_args_from_caller_to_enzyme<'ll>(
|
|||
// int2 >= int1, which means the shadow vector is large enough to store the gradient.
|
||||
assert_eq!(cx.type_kind(next_outer_ty), TypeKind::Integer);
|
||||
|
||||
for i in 0..(width as usize) {
|
||||
let iterations =
|
||||
if matches!(diff_activity, DiffActivity::Dualv) { 1 } else { width as usize };
|
||||
|
||||
for i in 0..iterations {
|
||||
let next_outer_arg2 = outer_args[outer_pos + 2 * (i + 1)];
|
||||
let next_outer_ty2 = cx.val_ty(next_outer_arg2);
|
||||
assert_eq!(cx.type_kind(next_outer_ty2), TypeKind::Pointer);
|
||||
|
|
@ -136,7 +163,7 @@ fn match_args_from_caller_to_enzyme<'ll>(
|
|||
}
|
||||
args.push(cx.get_metadata_value(enzyme_const));
|
||||
args.push(next_outer_arg);
|
||||
outer_pos += 2 + 2 * width as usize;
|
||||
outer_pos += 2 + 2 * iterations;
|
||||
activity_pos += 2;
|
||||
} else {
|
||||
// A duplicated pointer will have the following two outer_fn arguments:
|
||||
|
|
@ -360,6 +387,7 @@ fn generate_enzyme_call<'ll>(
|
|||
let outer_args: Vec<&llvm::Value> = get_params(outer_fn);
|
||||
match_args_from_caller_to_enzyme(
|
||||
&cx,
|
||||
&builder,
|
||||
attrs.width,
|
||||
&mut args,
|
||||
&attrs.input_activity,
|
||||
|
|
|
|||
|
|
@ -247,6 +247,16 @@ pub(super) fn stub<'ll, 'tcx>(
|
|||
StubInfo { metadata, unique_type_id }
|
||||
}
|
||||
|
||||
struct AdtStackPopGuard<'ll, 'tcx, 'a> {
|
||||
cx: &'a CodegenCx<'ll, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'ll, 'tcx, 'a> Drop for AdtStackPopGuard<'ll, 'tcx, 'a> {
|
||||
fn drop(&mut self) {
|
||||
debug_context(self.cx).adt_stack.borrow_mut().pop();
|
||||
}
|
||||
}
|
||||
|
||||
/// This function enables creating debuginfo nodes that can recursively refer to themselves.
|
||||
/// It will first insert the given stub into the type map and only then execute the `members`
|
||||
/// and `generics` closures passed in. These closures have access to the stub so they can
|
||||
|
|
@ -261,6 +271,44 @@ pub(super) fn build_type_with_children<'ll, 'tcx>(
|
|||
) -> DINodeCreationResult<'ll> {
|
||||
assert_eq!(debug_context(cx).type_map.di_node_for_unique_id(stub_info.unique_type_id), None);
|
||||
|
||||
let mut _adt_stack_pop_guard = None;
|
||||
if let UniqueTypeId::Ty(ty, ..) = stub_info.unique_type_id
|
||||
&& let ty::Adt(adt_def, args) = ty.kind()
|
||||
{
|
||||
let def_id = adt_def.did();
|
||||
// If any sub type reference the original type definition and the sub type has a type
|
||||
// parameter that strictly contains the original parameter, the original type is a recursive
|
||||
// type that can expanding indefinitely. Example,
|
||||
// ```
|
||||
// enum Recursive<T> {
|
||||
// Recurse(*const Recursive<Wrap<T>>),
|
||||
// Item(T),
|
||||
// }
|
||||
// ```
|
||||
let is_expanding_recursive =
|
||||
debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| {
|
||||
if def_id == *parent_def_id {
|
||||
args.iter().zip(parent_args.iter()).any(|(arg, parent_arg)| {
|
||||
if let (Some(arg), Some(parent_arg)) = (arg.as_type(), parent_arg.as_type())
|
||||
{
|
||||
arg != parent_arg && arg.contains(parent_arg)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
if is_expanding_recursive {
|
||||
// FIXME: indicate that this is an expanding recursive type in stub metadata?
|
||||
return DINodeCreationResult::new(stub_info.metadata, false);
|
||||
} else {
|
||||
debug_context(cx).adt_stack.borrow_mut().push((def_id, args));
|
||||
_adt_stack_pop_guard = Some(AdtStackPopGuard { cx });
|
||||
}
|
||||
}
|
||||
|
||||
debug_context(cx).type_map.insert(stub_info.unique_type_id, stub_info.metadata);
|
||||
|
||||
let members: SmallVec<_> =
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> {
|
|||
created_files: RefCell<UnordMap<Option<(StableSourceFileId, SourceFileHash)>, &'ll DIFile>>,
|
||||
|
||||
type_map: metadata::TypeMap<'ll, 'tcx>,
|
||||
adt_stack: RefCell<Vec<(DefId, GenericArgsRef<'tcx>)>>,
|
||||
namespace_map: RefCell<DefIdMap<&'ll DIScope>>,
|
||||
recursion_marker_type: OnceCell<&'ll DIType>,
|
||||
}
|
||||
|
|
@ -80,6 +81,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
|
|||
builder,
|
||||
created_files: Default::default(),
|
||||
type_map: Default::default(),
|
||||
adt_stack: Default::default(),
|
||||
namespace_map: RefCell::new(Default::default()),
|
||||
recursion_marker_type: OnceCell::new(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ use crate::check_consts::is_fn_or_trait_safe_to_expose_on_stable;
|
|||
use crate::errors;
|
||||
|
||||
type QualifResults<'mir, 'tcx, Q> =
|
||||
rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>;
|
||||
rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'tcx, Q>>;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum ConstConditionsHold {
|
||||
|
|
|
|||
|
|
@ -22,17 +22,17 @@ use super::{ConstCx, Qualif, qualifs};
|
|||
/// qualified immediately after it is borrowed or its address escapes. The borrow must allow for
|
||||
/// mutation, which includes shared borrows of places with interior mutability. The type of
|
||||
/// borrowed place must contain the qualif.
|
||||
struct TransferFunction<'a, 'mir, 'tcx, Q> {
|
||||
ccx: &'a ConstCx<'mir, 'tcx>,
|
||||
state: &'a mut State,
|
||||
struct TransferFunction<'mir, 'tcx, Q> {
|
||||
ccx: &'mir ConstCx<'mir, 'tcx>,
|
||||
state: &'mir mut State,
|
||||
_qualif: PhantomData<Q>,
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, Q> TransferFunction<'a, 'mir, 'tcx, Q>
|
||||
impl<'mir, 'tcx, Q> TransferFunction<'mir, 'tcx, Q>
|
||||
where
|
||||
Q: Qualif,
|
||||
{
|
||||
fn new(ccx: &'a ConstCx<'mir, 'tcx>, state: &'a mut State) -> Self {
|
||||
fn new(ccx: &'mir ConstCx<'mir, 'tcx>, state: &'mir mut State) -> Self {
|
||||
TransferFunction { ccx, state, _qualif: PhantomData }
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Q> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx, Q>
|
||||
impl<'tcx, Q> Visitor<'tcx> for TransferFunction<'_, 'tcx, Q>
|
||||
where
|
||||
Q: Qualif,
|
||||
{
|
||||
|
|
@ -228,20 +228,20 @@ where
|
|||
}
|
||||
|
||||
/// The dataflow analysis used to propagate qualifs on arbitrary CFGs.
|
||||
pub(super) struct FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> {
|
||||
ccx: &'a ConstCx<'mir, 'tcx>,
|
||||
pub(super) struct FlowSensitiveAnalysis<'mir, 'tcx, Q> {
|
||||
ccx: &'mir ConstCx<'mir, 'tcx>,
|
||||
_qualif: PhantomData<Q>,
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, Q> FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>
|
||||
impl<'mir, 'tcx, Q> FlowSensitiveAnalysis<'mir, 'tcx, Q>
|
||||
where
|
||||
Q: Qualif,
|
||||
{
|
||||
pub(super) fn new(_: Q, ccx: &'a ConstCx<'mir, 'tcx>) -> Self {
|
||||
pub(super) fn new(_: Q, ccx: &'mir ConstCx<'mir, 'tcx>) -> Self {
|
||||
FlowSensitiveAnalysis { ccx, _qualif: PhantomData }
|
||||
}
|
||||
|
||||
fn transfer_function(&self, state: &'a mut State) -> TransferFunction<'a, 'mir, 'tcx, Q> {
|
||||
fn transfer_function(&self, state: &'mir mut State) -> TransferFunction<'mir, 'tcx, Q> {
|
||||
TransferFunction::<Q>::new(self.ccx, state)
|
||||
}
|
||||
}
|
||||
|
|
@ -313,7 +313,7 @@ impl JoinSemiLattice for State {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
|
||||
impl<'tcx, Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, 'tcx, Q>
|
||||
where
|
||||
Q: Qualif,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
/// Call this on things you got out of the MIR (so it is as generic as the current
|
||||
/// stack frame), to bring it into the proper environment for this interpreter.
|
||||
pub(super) fn instantiate_from_current_frame_and_normalize_erasing_regions<
|
||||
pub fn instantiate_from_current_frame_and_normalize_erasing_regions<
|
||||
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||
>(
|
||||
&self,
|
||||
|
|
@ -279,9 +279,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
/// Call this on things you got out of the MIR (so it is as generic as the provided
|
||||
/// stack frame), to bring it into the proper environment for this interpreter.
|
||||
pub(super) fn instantiate_from_frame_and_normalize_erasing_regions<
|
||||
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||
>(
|
||||
pub fn instantiate_from_frame_and_normalize_erasing_regions<T: TypeFoldable<TyCtxt<'tcx>>>(
|
||||
&self,
|
||||
frame: &Frame<'tcx, M::Provenance, M::FrameExtra>,
|
||||
value: T,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ use super::{
|
|||
AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok,
|
||||
};
|
||||
use crate::const_eval;
|
||||
use crate::const_eval::DummyMachine;
|
||||
use crate::errors::NestedStaticInThreadLocal;
|
||||
|
||||
pub trait CompileTimeMachine<'tcx, T> = Machine<
|
||||
|
|
@ -323,14 +324,17 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
impl<'tcx, M: super::intern::CompileTimeMachine<'tcx, !>> InterpCx<'tcx, M> {
|
||||
impl<'tcx> InterpCx<'tcx, DummyMachine> {
|
||||
/// A helper function that allocates memory for the layout given and gives you access to mutate
|
||||
/// it. Once your own mutation code is done, the backing `Allocation` is removed from the
|
||||
/// current `Memory` and interned as read-only into the global memory.
|
||||
pub fn intern_with_temp_alloc(
|
||||
&mut self,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
f: impl FnOnce(&mut InterpCx<'tcx, M>, &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, ()>,
|
||||
f: impl FnOnce(
|
||||
&mut InterpCx<'tcx, DummyMachine>,
|
||||
&PlaceTy<'tcx, CtfeProvenance>,
|
||||
) -> InterpResult<'tcx, ()>,
|
||||
) -> InterpResult<'tcx, AllocId> {
|
||||
// `allocate` picks a fresh AllocId that we will associate with its data below.
|
||||
let dest = self.allocate(layout, MemoryKind::Stack)?;
|
||||
|
|
|
|||
|
|
@ -1502,16 +1502,21 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
let mut projections = base_place.place.projections;
|
||||
|
||||
let node_ty = self.cx.typeck_results().node_type(node);
|
||||
// Opaque types can't have field projections, but we can instead convert
|
||||
// the current place in-place (heh) to the hidden type, and then apply all
|
||||
// follow up projections on that.
|
||||
if node_ty != place_ty
|
||||
&& self
|
||||
.cx
|
||||
.try_structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), place_ty)
|
||||
.is_impl_trait()
|
||||
{
|
||||
projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
|
||||
if !self.cx.tcx().next_trait_solver_globally() {
|
||||
// Opaque types can't have field projections, but we can instead convert
|
||||
// the current place in-place (heh) to the hidden type, and then apply all
|
||||
// follow up projections on that.
|
||||
if node_ty != place_ty
|
||||
&& self
|
||||
.cx
|
||||
.try_structurally_resolve_type(
|
||||
self.cx.tcx().hir_span(base_place.hir_id),
|
||||
place_ty,
|
||||
)
|
||||
.is_impl_trait()
|
||||
{
|
||||
projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
|
||||
}
|
||||
}
|
||||
projections.push(Projection { kind, ty });
|
||||
PlaceWithHirId::new(node, base_place.place.base_ty, base_place.place.base, projections)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::ops::{Index, IndexMut, RangeBounds};
|
||||
use std::slice::GetDisjointMutError::*;
|
||||
use std::slice::{self, SliceIndex};
|
||||
|
||||
|
|
@ -104,6 +104,17 @@ impl<I: Idx, T> IndexSlice<I, T> {
|
|||
self.raw.swap(a.index(), b.index())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn copy_within(
|
||||
&mut self,
|
||||
src: impl IntoSliceIdx<I, [T], Output: RangeBounds<usize>>,
|
||||
dest: I,
|
||||
) where
|
||||
T: Copy,
|
||||
{
|
||||
self.raw.copy_within(src.into_slice_idx(), dest.index());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get<R: IntoSliceIdx<I, [T]>>(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ pub enum ProjectionKind {
|
|||
|
||||
/// A conversion from an opaque type to its hidden type so we can
|
||||
/// do further projections on it.
|
||||
///
|
||||
/// This is unused if `-Znext-solver` is enabled.
|
||||
OpaqueCast,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1242,6 +1242,8 @@ pub enum ProjectionElem<V, T> {
|
|||
|
||||
/// Like an explicit cast from an opaque type to a concrete type, but without
|
||||
/// requiring an intermediate variable.
|
||||
///
|
||||
/// This is unused with `-Znext-solver`.
|
||||
OpaqueCast(T),
|
||||
|
||||
/// A transmute from an unsafe binder to the type that it wraps. This is a projection
|
||||
|
|
|
|||
|
|
@ -101,18 +101,21 @@ impl<'tcx> MatchPairTree<'tcx> {
|
|||
place_builder = resolved;
|
||||
}
|
||||
|
||||
// Only add the OpaqueCast projection if the given place is an opaque type and the
|
||||
// expected type from the pattern is not.
|
||||
let may_need_cast = match place_builder.base() {
|
||||
PlaceBase::Local(local) => {
|
||||
let ty =
|
||||
Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx).ty;
|
||||
ty != pattern.ty && ty.has_opaque_types()
|
||||
if !cx.tcx.next_trait_solver_globally() {
|
||||
// Only add the OpaqueCast projection if the given place is an opaque type and the
|
||||
// expected type from the pattern is not.
|
||||
let may_need_cast = match place_builder.base() {
|
||||
PlaceBase::Local(local) => {
|
||||
let ty =
|
||||
Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
|
||||
.ty;
|
||||
ty != pattern.ty && ty.has_opaque_types()
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
if may_need_cast {
|
||||
place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
if may_need_cast {
|
||||
place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
|
||||
}
|
||||
|
||||
let place = place_builder.try_to_place(cx);
|
||||
|
|
|
|||
|
|
@ -39,20 +39,22 @@ impl<'tcx> MutVisitor<'tcx> for PostAnalysisNormalizeVisitor<'tcx> {
|
|||
_context: PlaceContext,
|
||||
_location: Location,
|
||||
) {
|
||||
// Performance optimization: don't reintern if there is no `OpaqueCast` to remove.
|
||||
if place.projection.iter().all(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_))) {
|
||||
return;
|
||||
if !self.tcx.next_trait_solver_globally() {
|
||||
// `OpaqueCast` projections are only needed if there are opaque types on which projections
|
||||
// are performed. After the `PostAnalysisNormalize` pass, all opaque types are replaced with their
|
||||
// hidden types, so we don't need these projections anymore.
|
||||
//
|
||||
// Performance optimization: don't reintern if there is no `OpaqueCast` to remove.
|
||||
if place.projection.iter().any(|elem| matches!(elem, ProjectionElem::OpaqueCast(_))) {
|
||||
place.projection = self.tcx.mk_place_elems(
|
||||
&place
|
||||
.projection
|
||||
.into_iter()
|
||||
.filter(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_)))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
};
|
||||
}
|
||||
// `OpaqueCast` projections are only needed if there are opaque types on which projections
|
||||
// are performed. After the `PostAnalysisNormalize` pass, all opaque types are replaced with their
|
||||
// hidden types, so we don't need these projections anymore.
|
||||
place.projection = self.tcx.mk_place_elems(
|
||||
&place
|
||||
.projection
|
||||
.into_iter()
|
||||
.filter(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_)))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
self.super_place(place, _context, _location);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,12 @@ fn wasm_abi_safe<'tcx>(tcx: TyCtxt<'tcx>, arg: &ArgAbi<'tcx, Ty<'tcx>>) -> bool
|
|||
return true;
|
||||
}
|
||||
|
||||
// Both the old and the new ABIs treat vector types like `v128` the same
|
||||
// way.
|
||||
if uses_vector_registers(&arg.mode, &arg.layout.backend_repr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// This matches `unwrap_trivial_aggregate` in the wasm ABI logic.
|
||||
if arg.layout.is_aggregate() {
|
||||
let cx = LayoutCx::new(tcx, TypingEnv::fully_monomorphized());
|
||||
|
|
@ -111,6 +117,11 @@ fn wasm_abi_safe<'tcx>(tcx: TyCtxt<'tcx>, arg: &ArgAbi<'tcx, Ty<'tcx>>) -> bool
|
|||
}
|
||||
}
|
||||
|
||||
// Zero-sized types are dropped in both ABIs, so they're safe
|
||||
if arg.layout.is_zst() {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use rustc_ast::expand::autodiff_attrs::{AutoDiffItem, DiffActivity};
|
|||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::mono::MonoItem;
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, Instance, PseudoCanonicalInput, Ty, TyCtxt, TypingEnv};
|
||||
use rustc_symbol_mangling::symbol_name_for_instance_in_crate;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
|
|
@ -22,23 +22,51 @@ fn adjust_activity_to_abi<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>, da: &mut Vec
|
|||
for (i, ty) in sig.inputs().iter().enumerate() {
|
||||
if let Some(inner_ty) = ty.builtin_deref(true) {
|
||||
if inner_ty.is_slice() {
|
||||
// Now we need to figure out the size of each slice element in memory to allow
|
||||
// safety checks and usability improvements in the backend.
|
||||
let sty = match inner_ty.builtin_index() {
|
||||
Some(sty) => sty,
|
||||
None => {
|
||||
panic!("slice element type unknown");
|
||||
}
|
||||
};
|
||||
let pci = PseudoCanonicalInput {
|
||||
typing_env: TypingEnv::fully_monomorphized(),
|
||||
value: sty,
|
||||
};
|
||||
|
||||
let layout = tcx.layout_of(pci);
|
||||
let elem_size = match layout {
|
||||
Ok(layout) => layout.size,
|
||||
Err(_) => {
|
||||
bug!("autodiff failed to compute slice element size");
|
||||
}
|
||||
};
|
||||
let elem_size: u32 = elem_size.bytes() as u32;
|
||||
|
||||
// We know that the length will be passed as extra arg.
|
||||
if !da.is_empty() {
|
||||
// We are looking at a slice. The length of that slice will become an
|
||||
// extra integer on llvm level. Integers are always const.
|
||||
// However, if the slice get's duplicated, we want to know to later check the
|
||||
// size. So we mark the new size argument as FakeActivitySize.
|
||||
// There is one FakeActivitySize per slice, so for convenience we store the
|
||||
// slice element size in bytes in it. We will use the size in the backend.
|
||||
let activity = match da[i] {
|
||||
DiffActivity::DualOnly
|
||||
| DiffActivity::Dual
|
||||
| DiffActivity::Dualv
|
||||
| DiffActivity::DuplicatedOnly
|
||||
| DiffActivity::Duplicated => DiffActivity::FakeActivitySize,
|
||||
| DiffActivity::Duplicated => {
|
||||
DiffActivity::FakeActivitySize(Some(elem_size))
|
||||
}
|
||||
DiffActivity::Const => DiffActivity::Const,
|
||||
_ => bug!("unexpected activity for ptr/ref"),
|
||||
};
|
||||
new_activities.push(activity);
|
||||
new_positions.push(i + 1);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,16 +92,20 @@ where
|
|||
let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else {
|
||||
panic!("expected object type in `probe_and_consider_object_bound_candidate`");
|
||||
};
|
||||
ecx.add_goals(
|
||||
GoalSource::ImplWhereBound,
|
||||
structural_traits::predicates_for_object_candidate(
|
||||
ecx,
|
||||
goal.param_env,
|
||||
goal.predicate.trait_ref(cx),
|
||||
bounds,
|
||||
),
|
||||
);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
match structural_traits::predicates_for_object_candidate(
|
||||
ecx,
|
||||
goal.param_env,
|
||||
goal.predicate.trait_ref(cx),
|
||||
bounds,
|
||||
) {
|
||||
Ok(requirements) => {
|
||||
ecx.add_goals(GoalSource::ImplWhereBound, requirements);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
Err(_) => {
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ use derive_where::derive_where;
|
|||
use rustc_type_ir::data_structures::HashMap;
|
||||
use rustc_type_ir::inherent::*;
|
||||
use rustc_type_ir::lang_items::TraitSolverLangItem;
|
||||
use rustc_type_ir::solve::inspect::ProbeKind;
|
||||
use rustc_type_ir::{
|
||||
self as ty, Interner, Movability, Mutability, TypeFoldable, TypeFolder, TypeSuperFoldable,
|
||||
Upcast as _, elaborate,
|
||||
self as ty, FallibleTypeFolder, Interner, Movability, Mutability, TypeFoldable,
|
||||
TypeSuperFoldable, Upcast as _, elaborate,
|
||||
};
|
||||
use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
|
||||
use tracing::instrument;
|
||||
|
|
@ -822,22 +823,16 @@ pub(in crate::solve) fn const_conditions_for_destruct<I: Interner>(
|
|||
/// impl Baz for dyn Foo<Item = Ty> {}
|
||||
/// ```
|
||||
///
|
||||
/// However, in order to make such impls well-formed, we need to do an
|
||||
/// However, in order to make such impls non-cyclical, we need to do an
|
||||
/// additional step of eagerly folding the associated types in the where
|
||||
/// clauses of the impl. In this example, that means replacing
|
||||
/// `<Self as Foo>::Bar` with `Ty` in the first impl.
|
||||
///
|
||||
// FIXME: This is only necessary as `<Self as Trait>::Assoc: ItemBound`
|
||||
// bounds in impls are trivially proven using the item bound candidates.
|
||||
// This is unsound in general and once that is fixed, we don't need to
|
||||
// normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9
|
||||
// for more details.
|
||||
pub(in crate::solve) fn predicates_for_object_candidate<D, I>(
|
||||
ecx: &EvalCtxt<'_, D>,
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
param_env: I::ParamEnv,
|
||||
trait_ref: ty::TraitRef<I>,
|
||||
object_bounds: I::BoundExistentialPredicates,
|
||||
) -> Vec<Goal<I, I::Predicate>>
|
||||
) -> Result<Vec<Goal<I, I::Predicate>>, Ambiguous>
|
||||
where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
|
|
@ -871,72 +866,130 @@ where
|
|||
.extend(cx.item_bounds(associated_type_def_id).iter_instantiated(cx, trait_ref.args));
|
||||
}
|
||||
|
||||
let mut replace_projection_with = HashMap::default();
|
||||
let mut replace_projection_with: HashMap<_, Vec<_>> = HashMap::default();
|
||||
for bound in object_bounds.iter() {
|
||||
if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() {
|
||||
// FIXME: We *probably* should replace this with a dummy placeholder,
|
||||
// b/c don't want to replace literal instances of this dyn type that
|
||||
// show up in the bounds, but just ones that come from substituting
|
||||
// `Self` with the dyn type.
|
||||
let proj = proj.with_self_ty(cx, trait_ref.self_ty());
|
||||
let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj));
|
||||
assert_eq!(
|
||||
old_ty,
|
||||
None,
|
||||
"{:?} has two generic parameters: {:?} and {:?}",
|
||||
proj.projection_term,
|
||||
proj.term,
|
||||
old_ty.unwrap()
|
||||
);
|
||||
replace_projection_with.entry(proj.def_id()).or_default().push(bound.rebind(proj));
|
||||
}
|
||||
}
|
||||
|
||||
let mut folder =
|
||||
ReplaceProjectionWith { ecx, param_env, mapping: replace_projection_with, nested: vec![] };
|
||||
let folded_requirements = requirements.fold_with(&mut folder);
|
||||
let mut folder = ReplaceProjectionWith {
|
||||
ecx,
|
||||
param_env,
|
||||
self_ty: trait_ref.self_ty(),
|
||||
mapping: &replace_projection_with,
|
||||
nested: vec![],
|
||||
};
|
||||
|
||||
folder
|
||||
let requirements = requirements.try_fold_with(&mut folder)?;
|
||||
Ok(folder
|
||||
.nested
|
||||
.into_iter()
|
||||
.chain(folded_requirements.into_iter().map(|clause| Goal::new(cx, param_env, clause)))
|
||||
.collect()
|
||||
.chain(requirements.into_iter().map(|clause| Goal::new(cx, param_env, clause)))
|
||||
.collect())
|
||||
}
|
||||
|
||||
struct ReplaceProjectionWith<'a, D: SolverDelegate<Interner = I>, I: Interner> {
|
||||
ecx: &'a EvalCtxt<'a, D>,
|
||||
struct ReplaceProjectionWith<'a, 'b, I: Interner, D: SolverDelegate<Interner = I>> {
|
||||
ecx: &'a mut EvalCtxt<'b, D>,
|
||||
param_env: I::ParamEnv,
|
||||
mapping: HashMap<I::DefId, ty::Binder<I, ty::ProjectionPredicate<I>>>,
|
||||
self_ty: I::Ty,
|
||||
mapping: &'a HashMap<I::DefId, Vec<ty::Binder<I, ty::ProjectionPredicate<I>>>>,
|
||||
nested: Vec<Goal<I, I::Predicate>>,
|
||||
}
|
||||
|
||||
impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I>
|
||||
for ReplaceProjectionWith<'_, D, I>
|
||||
impl<D, I> ReplaceProjectionWith<'_, '_, I, D>
|
||||
where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
fn projection_may_match(
|
||||
&mut self,
|
||||
source_projection: ty::Binder<I, ty::ProjectionPredicate<I>>,
|
||||
target_projection: ty::AliasTerm<I>,
|
||||
) -> bool {
|
||||
source_projection.item_def_id() == target_projection.def_id
|
||||
&& self
|
||||
.ecx
|
||||
.probe(|_| ProbeKind::ProjectionCompatibility)
|
||||
.enter(|ecx| -> Result<_, NoSolution> {
|
||||
let source_projection = ecx.instantiate_binder_with_infer(source_projection);
|
||||
ecx.eq(self.param_env, source_projection.projection_term, target_projection)?;
|
||||
ecx.try_evaluate_added_goals()
|
||||
})
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// Try to replace an alias with the term present in the projection bounds of the self type.
|
||||
/// Returns `Ok<None>` if this alias is not eligible to be replaced, or bail with
|
||||
/// `Err(Ambiguous)` if it's uncertain which projection bound to replace the term with due
|
||||
/// to multiple bounds applying.
|
||||
fn try_eagerly_replace_alias(
|
||||
&mut self,
|
||||
alias_term: ty::AliasTerm<I>,
|
||||
) -> Result<Option<I::Term>, Ambiguous> {
|
||||
if alias_term.self_ty() != self.self_ty {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Some(replacements) = self.mapping.get(&alias_term.def_id) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// This is quite similar to the `projection_may_match` we use in unsizing,
|
||||
// but here we want to unify a projection predicate against an alias term
|
||||
// so we can replace it with the the projection predicate's term.
|
||||
let mut matching_projections = replacements
|
||||
.iter()
|
||||
.filter(|source_projection| self.projection_may_match(**source_projection, alias_term));
|
||||
let Some(replacement) = matching_projections.next() else {
|
||||
// This shouldn't happen.
|
||||
panic!("could not replace {alias_term:?} with term from from {:?}", self.self_ty);
|
||||
};
|
||||
// FIXME: This *may* have issues with duplicated projections.
|
||||
if matching_projections.next().is_some() {
|
||||
// If there's more than one projection that we can unify here, then we
|
||||
// need to stall until inference constrains things so that there's only
|
||||
// one choice.
|
||||
return Err(Ambiguous);
|
||||
}
|
||||
|
||||
let replacement = self.ecx.instantiate_binder_with_infer(*replacement);
|
||||
self.nested.extend(
|
||||
self.ecx
|
||||
.eq_and_get_goals(self.param_env, alias_term, replacement.projection_term)
|
||||
.expect("expected to be able to unify goal projection with dyn's projection"),
|
||||
);
|
||||
|
||||
Ok(Some(replacement.term))
|
||||
}
|
||||
}
|
||||
|
||||
/// Marker for bailing with ambiguity.
|
||||
pub(crate) struct Ambiguous;
|
||||
|
||||
impl<D, I> FallibleTypeFolder<I> for ReplaceProjectionWith<'_, '_, I, D>
|
||||
where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
type Error = Ambiguous;
|
||||
|
||||
fn cx(&self) -> I {
|
||||
self.ecx.cx()
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
|
||||
fn try_fold_ty(&mut self, ty: I::Ty) -> Result<I::Ty, Ambiguous> {
|
||||
if let ty::Alias(ty::Projection, alias_ty) = ty.kind() {
|
||||
if let Some(replacement) = self.mapping.get(&alias_ty.def_id) {
|
||||
// We may have a case where our object type's projection bound is higher-ranked,
|
||||
// but the where clauses we instantiated are not. We can solve this by instantiating
|
||||
// the binder at the usage site.
|
||||
let proj = self.ecx.instantiate_binder_with_infer(*replacement);
|
||||
// FIXME: Technically this equate could be fallible...
|
||||
self.nested.extend(
|
||||
self.ecx
|
||||
.eq_and_get_goals(
|
||||
self.param_env,
|
||||
alias_ty,
|
||||
proj.projection_term.expect_ty(self.ecx.cx()),
|
||||
)
|
||||
.expect(
|
||||
"expected to be able to unify goal projection with dyn's projection",
|
||||
),
|
||||
);
|
||||
proj.term.expect_ty()
|
||||
} else {
|
||||
ty.super_fold_with(self)
|
||||
if let Some(term) = self.try_eagerly_replace_alias(alias_ty.into())? {
|
||||
return Ok(term.expect_ty());
|
||||
}
|
||||
} else {
|
||||
ty.super_fold_with(self)
|
||||
}
|
||||
|
||||
ty.try_super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -944,7 +944,7 @@ where
|
|||
target_projection: ty::Binder<I, ty::ExistentialProjection<I>>| {
|
||||
source_projection.item_def_id() == target_projection.item_def_id()
|
||||
&& ecx
|
||||
.probe(|_| ProbeKind::UpcastProjectionCompatibility)
|
||||
.probe(|_| ProbeKind::ProjectionCompatibility)
|
||||
.enter(|ecx| -> Result<_, NoSolution> {
|
||||
ecx.enter_forall(target_projection, |ecx, target_projection| {
|
||||
let source_projection =
|
||||
|
|
|
|||
|
|
@ -2065,7 +2065,8 @@ fn collect_print_requests(
|
|||
check_print_request_stability(early_dcx, unstable_opts, (print_name, *print_kind));
|
||||
*print_kind
|
||||
} else {
|
||||
emit_unknown_print_request_help(early_dcx, req)
|
||||
let is_nightly = nightly_options::match_is_nightly_build(matches);
|
||||
emit_unknown_print_request_help(early_dcx, req, is_nightly)
|
||||
};
|
||||
|
||||
let out = out.unwrap_or(OutFileName::Stdout);
|
||||
|
|
@ -2089,25 +2090,37 @@ fn check_print_request_stability(
|
|||
unstable_opts: &UnstableOptions,
|
||||
(print_name, print_kind): (&str, PrintKind),
|
||||
) {
|
||||
if !is_print_request_stable(print_kind) && !unstable_opts.unstable_options {
|
||||
early_dcx.early_fatal(format!(
|
||||
"the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \
|
||||
print option"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn is_print_request_stable(print_kind: PrintKind) -> bool {
|
||||
match print_kind {
|
||||
PrintKind::AllTargetSpecsJson
|
||||
| PrintKind::CheckCfg
|
||||
| PrintKind::CrateRootLintLevels
|
||||
| PrintKind::SupportedCrateTypes
|
||||
| PrintKind::TargetSpecJson
|
||||
if !unstable_opts.unstable_options =>
|
||||
{
|
||||
early_dcx.early_fatal(format!(
|
||||
"the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \
|
||||
print option"
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
| PrintKind::TargetSpecJson => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str) -> ! {
|
||||
let prints = PRINT_KINDS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>();
|
||||
fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! {
|
||||
let prints = PRINT_KINDS
|
||||
.iter()
|
||||
.filter_map(|(name, kind)| {
|
||||
// If we're not on nightly, we don't want to print unstable options
|
||||
if !is_nightly && !is_print_request_stable(*kind) {
|
||||
None
|
||||
} else {
|
||||
Some(format!("`{name}`"))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let prints = prints.join(", ");
|
||||
|
||||
let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`"));
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
|
|||
inspect::ProbeStep::NestedProbe(ref probe) => {
|
||||
match probe.kind {
|
||||
// These never assemble candidates for the goal we're trying to solve.
|
||||
inspect::ProbeKind::UpcastProjectionCompatibility
|
||||
inspect::ProbeKind::ProjectionCompatibility
|
||||
| inspect::ProbeKind::ShadowedEnvProbing => continue,
|
||||
|
||||
inspect::ProbeKind::NormalizedSelfTyAssembly
|
||||
|
|
@ -314,8 +314,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
|
|||
}
|
||||
|
||||
match probe.kind {
|
||||
inspect::ProbeKind::UpcastProjectionCompatibility
|
||||
| inspect::ProbeKind::ShadowedEnvProbing => bug!(),
|
||||
inspect::ProbeKind::ProjectionCompatibility
|
||||
| inspect::ProbeKind::ShadowedEnvProbing => {
|
||||
bug!()
|
||||
}
|
||||
|
||||
inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly => {}
|
||||
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ fn to_selection<'tcx>(
|
|||
},
|
||||
ProbeKind::NormalizedSelfTyAssembly
|
||||
| ProbeKind::UnsizeAssembly
|
||||
| ProbeKind::UpcastProjectionCompatibility
|
||||
| ProbeKind::ProjectionCompatibility
|
||||
| ProbeKind::OpaqueTypeStorageLookup { result: _ }
|
||||
| ProbeKind::Root { result: _ }
|
||||
| ProbeKind::ShadowedEnvProbing
|
||||
|
|
|
|||
|
|
@ -118,10 +118,12 @@ pub enum ProbeKind<I: Interner> {
|
|||
/// Used in the probe that wraps normalizing the non-self type for the unsize
|
||||
/// trait, which is also structurally matched on.
|
||||
UnsizeAssembly,
|
||||
/// During upcasting from some source object to target object type, used to
|
||||
/// do a probe to find out what projection type(s) may be used to prove that
|
||||
/// the source type upholds all of the target type's object bounds.
|
||||
UpcastProjectionCompatibility,
|
||||
/// Used to do a probe to find out what projection type(s) match a given
|
||||
/// alias bound or projection predicate. For trait upcasting, this is used
|
||||
/// to prove that the source type upholds all of the target type's object
|
||||
/// bounds. For object type bounds, this is used when eagerly replacing
|
||||
/// supertrait aliases.
|
||||
ProjectionCompatibility,
|
||||
/// Looking for param-env candidates that satisfy the trait ref for a projection.
|
||||
ShadowedEnvProbing,
|
||||
/// Try to unify an opaque type with an existing key in the storage.
|
||||
|
|
|
|||
0
diff
0
diff
|
|
@ -67,9 +67,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.153"
|
||||
version = "0.1.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926ef6a360c15a911023352fd6969c51605d70495406f735beb1ca0257448e59"
|
||||
checksum = "341e0830ca6170a4fcf02e92e57daf4b6f10142d48da32a547023867a6c8b35e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"rustc-std-workspace-core",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ bench = false
|
|||
|
||||
[dependencies]
|
||||
core = { path = "../core", public = true }
|
||||
compiler_builtins = { version = "=0.1.153", features = ['rustc-dep-of-std'] }
|
||||
compiler_builtins = { version = "=0.1.155", features = ['rustc-dep-of-std'] }
|
||||
|
||||
[features]
|
||||
compiler-builtins-mem = ['compiler_builtins/mem']
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
//!
|
||||
//! Hints may be compile time or runtime.
|
||||
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::{intrinsics, ub_checks};
|
||||
|
||||
/// Informs the compiler that the site which is calling this function is not
|
||||
|
|
@ -735,9 +736,9 @@ pub const fn cold_path() {
|
|||
crate::intrinsics::cold_path()
|
||||
}
|
||||
|
||||
/// Returns either `true_val` or `false_val` depending on the value of `b`,
|
||||
/// with a hint to the compiler that `b` is unlikely to be correctly
|
||||
/// predicted by a CPU’s branch predictor.
|
||||
/// Returns either `true_val` or `false_val` depending on the value of
|
||||
/// `condition`, with a hint to the compiler that `condition` is unlikely to be
|
||||
/// correctly predicted by a CPU’s branch predictor.
|
||||
///
|
||||
/// This method is functionally equivalent to
|
||||
/// ```ignore (this is just for illustrative purposes)
|
||||
|
|
@ -753,10 +754,10 @@ pub const fn cold_path() {
|
|||
/// search.
|
||||
///
|
||||
/// Note however that this lowering is not guaranteed (on any platform) and
|
||||
/// should not be relied upon when trying to write constant-time code. Also
|
||||
/// be aware that this lowering might *decrease* performance if `condition`
|
||||
/// is well-predictable. It is advisable to perform benchmarks to tell if
|
||||
/// this function is useful.
|
||||
/// should not be relied upon when trying to write cryptographic constant-time
|
||||
/// code. Also be aware that this lowering might *decrease* performance if
|
||||
/// `condition` is well-predictable. It is advisable to perform benchmarks to
|
||||
/// tell if this function is useful.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -780,6 +781,17 @@ pub const fn cold_path() {
|
|||
/// ```
|
||||
#[inline(always)]
|
||||
#[unstable(feature = "select_unpredictable", issue = "133962")]
|
||||
pub fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
|
||||
crate::intrinsics::select_unpredictable(b, true_val, false_val)
|
||||
pub fn select_unpredictable<T>(condition: bool, true_val: T, false_val: T) -> T {
|
||||
// FIXME(https://github.com/rust-lang/unsafe-code-guidelines/issues/245):
|
||||
// Change this to use ManuallyDrop instead.
|
||||
let mut true_val = MaybeUninit::new(true_val);
|
||||
let mut false_val = MaybeUninit::new(false_val);
|
||||
// SAFETY: The value that is not selected is dropped, and the selected one
|
||||
// is returned. This is necessary because the intrinsic doesn't drop the
|
||||
// value that is not selected.
|
||||
unsafe {
|
||||
crate::intrinsics::select_unpredictable(!condition, &mut true_val, &mut false_val)
|
||||
.assume_init_drop();
|
||||
crate::intrinsics::select_unpredictable(condition, true_val, false_val).assume_init()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1327,6 +1327,8 @@ pub const fn unlikely(b: bool) -> bool {
|
|||
/// any safety invariants.
|
||||
///
|
||||
/// The public form of this instrinsic is [`core::hint::select_unpredictable`].
|
||||
/// However unlike the public form, the intrinsic will not drop the value that
|
||||
/// is not selected.
|
||||
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_nounwind]
|
||||
|
|
|
|||
|
|
@ -224,14 +224,16 @@ impl f128 {
|
|||
|
||||
/// Not a Number (NaN).
|
||||
///
|
||||
/// Note that IEEE 754 doesn't define just a single NaN value;
|
||||
/// a plethora of bit patterns are considered to be NaN.
|
||||
/// Furthermore, the standard makes a difference
|
||||
/// between a "signaling" and a "quiet" NaN,
|
||||
/// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
|
||||
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
|
||||
/// and the stability of its representation over Rust versions
|
||||
/// and target platforms isn't guaranteed.
|
||||
/// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are
|
||||
/// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and
|
||||
/// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern)
|
||||
/// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more
|
||||
/// info.
|
||||
///
|
||||
/// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions
|
||||
/// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is
|
||||
/// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary.
|
||||
/// The concrete bit pattern may change across Rust versions and target platforms.
|
||||
#[allow(clippy::eq_op)]
|
||||
#[rustc_diagnostic_item = "f128_nan"]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
|
|
|
|||
|
|
@ -219,14 +219,16 @@ impl f16 {
|
|||
|
||||
/// Not a Number (NaN).
|
||||
///
|
||||
/// Note that IEEE 754 doesn't define just a single NaN value;
|
||||
/// a plethora of bit patterns are considered to be NaN.
|
||||
/// Furthermore, the standard makes a difference
|
||||
/// between a "signaling" and a "quiet" NaN,
|
||||
/// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
|
||||
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
|
||||
/// and the stability of its representation over Rust versions
|
||||
/// and target platforms isn't guaranteed.
|
||||
/// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are
|
||||
/// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and
|
||||
/// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern)
|
||||
/// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more
|
||||
/// info.
|
||||
///
|
||||
/// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions
|
||||
/// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is
|
||||
/// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary.
|
||||
/// The concrete bit pattern may change across Rust versions and target platforms.
|
||||
#[allow(clippy::eq_op)]
|
||||
#[rustc_diagnostic_item = "f16_nan"]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
|
|
|
|||
|
|
@ -470,14 +470,16 @@ impl f32 {
|
|||
|
||||
/// Not a Number (NaN).
|
||||
///
|
||||
/// Note that IEEE 754 doesn't define just a single NaN value;
|
||||
/// a plethora of bit patterns are considered to be NaN.
|
||||
/// Furthermore, the standard makes a difference
|
||||
/// between a "signaling" and a "quiet" NaN,
|
||||
/// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
|
||||
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
|
||||
/// and the stability of its representation over Rust versions
|
||||
/// and target platforms isn't guaranteed.
|
||||
/// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are
|
||||
/// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and
|
||||
/// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern)
|
||||
/// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more
|
||||
/// info.
|
||||
///
|
||||
/// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions
|
||||
/// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is
|
||||
/// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary.
|
||||
/// The concrete bit pattern may change across Rust versions and target platforms.
|
||||
#[stable(feature = "assoc_int_consts", since = "1.43.0")]
|
||||
#[rustc_diagnostic_item = "f32_nan"]
|
||||
#[allow(clippy::eq_op)]
|
||||
|
|
|
|||
|
|
@ -469,14 +469,16 @@ impl f64 {
|
|||
|
||||
/// Not a Number (NaN).
|
||||
///
|
||||
/// Note that IEEE 754 doesn't define just a single NaN value;
|
||||
/// a plethora of bit patterns are considered to be NaN.
|
||||
/// Furthermore, the standard makes a difference
|
||||
/// between a "signaling" and a "quiet" NaN,
|
||||
/// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
|
||||
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
|
||||
/// and the stability of its representation over Rust versions
|
||||
/// and target platforms isn't guaranteed.
|
||||
/// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are
|
||||
/// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and
|
||||
/// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern)
|
||||
/// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more
|
||||
/// info.
|
||||
///
|
||||
/// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions
|
||||
/// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is
|
||||
/// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary.
|
||||
/// The concrete bit pattern may change across Rust versions and target platforms.
|
||||
#[rustc_diagnostic_item = "f64_nan"]
|
||||
#[stable(feature = "assoc_int_consts", since = "1.43.0")]
|
||||
#[allow(clippy::eq_op)]
|
||||
|
|
|
|||
23
library/coretests/tests/hint.rs
Normal file
23
library/coretests/tests/hint.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#[test]
|
||||
fn select_unpredictable_drop() {
|
||||
use core::cell::Cell;
|
||||
struct X<'a>(&'a Cell<bool>);
|
||||
impl Drop for X<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.0.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
let a_dropped = Cell::new(false);
|
||||
let b_dropped = Cell::new(false);
|
||||
let a = X(&a_dropped);
|
||||
let b = X(&b_dropped);
|
||||
assert!(!a_dropped.get());
|
||||
assert!(!b_dropped.get());
|
||||
let selected = core::hint::select_unpredictable(core::hint::black_box(true), a, b);
|
||||
assert!(!a_dropped.get());
|
||||
assert!(b_dropped.get());
|
||||
drop(selected);
|
||||
assert!(a_dropped.get());
|
||||
assert!(b_dropped.get());
|
||||
}
|
||||
|
|
@ -68,6 +68,7 @@
|
|||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(portable_simd)]
|
||||
#![feature(ptr_metadata)]
|
||||
#![feature(select_unpredictable)]
|
||||
#![feature(slice_from_ptr_range)]
|
||||
#![feature(slice_internals)]
|
||||
#![feature(slice_partition_dedup)]
|
||||
|
|
@ -147,6 +148,7 @@ mod ffi;
|
|||
mod fmt;
|
||||
mod future;
|
||||
mod hash;
|
||||
mod hint;
|
||||
mod intrinsics;
|
||||
mod io;
|
||||
mod iter;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
|
|||
panic_unwind = { path = "../panic_unwind", optional = true }
|
||||
panic_abort = { path = "../panic_abort" }
|
||||
core = { path = "../core", public = true }
|
||||
compiler_builtins = { version = "=0.1.153" }
|
||||
compiler_builtins = { version = "=0.1.155" }
|
||||
unwind = { path = "../unwind" }
|
||||
hashbrown = { version = "0.15", default-features = false, features = [
|
||||
'rustc-dep-of-std',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Change this file to make users of the `download-ci-llvm` configuration download
|
||||
a new version of LLVM from CI, even if the LLVM submodule hasn’t changed.
|
||||
|
||||
Last change is for: https://github.com/rust-lang/rust/pull/138784
|
||||
Last change is for: https://github.com/rust-lang/rust/pull/139931
|
||||
|
|
|
|||
|
|
@ -370,8 +370,8 @@ impl Step for Llvm {
|
|||
cfg.define("LLVM_PROFDATA_FILE", path);
|
||||
}
|
||||
|
||||
// Libraries for ELF section compression.
|
||||
if !target.is_windows() {
|
||||
// Libraries for ELF section compression and profraw files merging.
|
||||
if !target.is_msvc() {
|
||||
cfg.define("LLVM_ENABLE_ZLIB", "ON");
|
||||
} else {
|
||||
cfg.define("LLVM_ENABLE_ZLIB", "OFF");
|
||||
|
|
|
|||
|
|
@ -22,21 +22,27 @@ where
|
|||
/// A wrapper around [`std::fs::remove_dir_all`] that can also be used on *non-directory entries*,
|
||||
/// including files and symbolic links.
|
||||
///
|
||||
/// - This will produce an error if the target path is not found.
|
||||
/// - This will not produce an error if the target path is not found.
|
||||
/// - Like [`std::fs::remove_dir_all`], this helper does not traverse symbolic links, will remove
|
||||
/// symbolic link itself.
|
||||
/// - This helper is **not** robust against races on the underlying filesystem, behavior is
|
||||
/// unspecified if this helper is called concurrently.
|
||||
/// - This helper is not robust against TOCTOU problems.
|
||||
///
|
||||
/// FIXME: this implementation is insufficiently robust to replace bootstrap's clean `rm_rf`
|
||||
/// implementation:
|
||||
///
|
||||
/// - This implementation currently does not perform retries.
|
||||
/// FIXME: Audit whether this implementation is robust enough to replace bootstrap's clean `rm_rf`.
|
||||
#[track_caller]
|
||||
pub fn recursive_remove<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||
let path = path.as_ref();
|
||||
let metadata = fs::symlink_metadata(path)?;
|
||||
|
||||
// If the path doesn't exist, we treat it as a successful no-op.
|
||||
// From the caller's perspective, the goal is simply "ensure this file/dir is gone" —
|
||||
// if it's already not there, that's a success, not an error.
|
||||
let metadata = match fs::symlink_metadata(path) {
|
||||
Ok(m) => m,
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()),
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
let is_dir_like = |meta: &fs::Metadata| {
|
||||
use std::os::windows::fs::FileTypeExt;
|
||||
|
|
@ -45,11 +51,35 @@ pub fn recursive_remove<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
|||
#[cfg(not(windows))]
|
||||
let is_dir_like = fs::Metadata::is_dir;
|
||||
|
||||
if is_dir_like(&metadata) {
|
||||
fs::remove_dir_all(path)
|
||||
} else {
|
||||
try_remove_op_set_perms(fs::remove_file, path, metadata)
|
||||
const MAX_RETRIES: usize = 5;
|
||||
const RETRY_DELAY_MS: u64 = 100;
|
||||
|
||||
let try_remove = || {
|
||||
if is_dir_like(&metadata) {
|
||||
fs::remove_dir_all(path)
|
||||
} else {
|
||||
try_remove_op_set_perms(fs::remove_file, path, metadata.clone())
|
||||
}
|
||||
};
|
||||
|
||||
// Retry deletion a few times to handle transient filesystem errors.
|
||||
// This is unusual for local file operations, but it's a mitigation
|
||||
// against unlikely events where malware scanners may be holding a
|
||||
// file beyond our control, to give the malware scanners some opportunity
|
||||
// to release their hold.
|
||||
for attempt in 0..MAX_RETRIES {
|
||||
match try_remove() {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()),
|
||||
Err(_) if attempt < MAX_RETRIES - 1 => {
|
||||
std::thread::sleep(std::time::Duration::from_millis(RETRY_DELAY_MS));
|
||||
continue;
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_remove_op_set_perms<'p, Op>(mut op: Op, path: &'p Path, metadata: Metadata) -> io::Result<()>
|
||||
|
|
@ -67,3 +97,9 @@ where
|
|||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_and_create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||
let path = path.as_ref();
|
||||
recursive_remove(path)?;
|
||||
fs::create_dir_all(path)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ mod recursive_remove_tests {
|
|||
let tmpdir = env::temp_dir();
|
||||
let path = tmpdir.join("__INTERNAL_BOOTSTRAP_nonexistent_path");
|
||||
assert!(fs::symlink_metadata(&path).is_err_and(|e| e.kind() == io::ErrorKind::NotFound));
|
||||
assert!(recursive_remove(&path).is_err_and(|e| e.kind() == io::ErrorKind::NotFound));
|
||||
assert!(recursive_remove(&path).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -175,6 +175,8 @@ See [compiletest directives] for a listing of directives.
|
|||
- For `ignore-*`/`needs-*`/`only-*` directives, unless extremely obvious,
|
||||
provide a brief remark on why the directive is needed. E.g. `"//@ ignore-wasi
|
||||
(wasi codegens the main symbol differently)"`.
|
||||
- When using `//@ ignore-auxiliary`, specify the corresponding main test files,
|
||||
e.g. ``//@ ignore-auxiliary (used by `./foo.rs`)``.
|
||||
|
||||
## FileCheck best practices
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,9 @@ means the test won't be compiled or run.
|
|||
* `ignore-X` where `X` is a target detail or other criteria on which to ignore the test (see below)
|
||||
* `only-X` is like `ignore-X`, but will *only* run the test on that target or
|
||||
stage
|
||||
* `ignore-auxiliary` is intended for files that *participate* in one or more other
|
||||
main test files but that `compiletest` should not try to build the file itself.
|
||||
Please backlink to which main test is actually using the auxiliary file.
|
||||
* `ignore-test` always ignores the test. This can be used to temporarily disable
|
||||
a test if it is currently not working, but you want to keep it in tree to
|
||||
re-enable it later.
|
||||
|
|
|
|||
|
|
@ -67,9 +67,13 @@ pub(crate) fn try_inline(
|
|||
record_extern_fqn(cx, did, ItemType::Trait);
|
||||
cx.with_param_env(did, |cx| {
|
||||
build_impls(cx, did, attrs_without_docs, &mut ret);
|
||||
clean::TraitItem(Box::new(build_external_trait(cx, did)))
|
||||
clean::TraitItem(Box::new(build_trait(cx, did)))
|
||||
})
|
||||
}
|
||||
Res::Def(DefKind::TraitAlias, did) => {
|
||||
record_extern_fqn(cx, did, ItemType::TraitAlias);
|
||||
cx.with_param_env(did, |cx| clean::TraitAliasItem(build_trait_alias(cx, did)))
|
||||
}
|
||||
Res::Def(DefKind::Fn, did) => {
|
||||
record_extern_fqn(cx, did, ItemType::Function);
|
||||
cx.with_param_env(did, |cx| {
|
||||
|
|
@ -251,7 +255,7 @@ pub(crate) fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemT
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean::Trait {
|
||||
pub(crate) fn build_trait(cx: &mut DocContext<'_>, did: DefId) -> clean::Trait {
|
||||
let trait_items = cx
|
||||
.tcx
|
||||
.associated_items(did)
|
||||
|
|
@ -263,11 +267,18 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean
|
|||
let predicates = cx.tcx.predicates_of(did);
|
||||
let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
|
||||
let generics = filter_non_trait_generics(did, generics);
|
||||
let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
|
||||
let (generics, supertrait_bounds) = separate_self_bounds(generics);
|
||||
clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds }
|
||||
}
|
||||
|
||||
pub(crate) fn build_function(cx: &mut DocContext<'_>, def_id: DefId) -> Box<clean::Function> {
|
||||
fn build_trait_alias(cx: &mut DocContext<'_>, did: DefId) -> clean::TraitAlias {
|
||||
let predicates = cx.tcx.predicates_of(did);
|
||||
let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
|
||||
let (generics, bounds) = separate_self_bounds(generics);
|
||||
clean::TraitAlias { generics, bounds }
|
||||
}
|
||||
|
||||
pub(super) fn build_function(cx: &mut DocContext<'_>, def_id: DefId) -> Box<clean::Function> {
|
||||
let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
|
||||
// The generics need to be cleaned before the signature.
|
||||
let mut generics =
|
||||
|
|
@ -788,12 +799,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:
|
|||
g
|
||||
}
|
||||
|
||||
/// Supertrait bounds for a trait are also listed in the generics coming from
|
||||
/// the metadata for a crate, so we want to separate those out and create a new
|
||||
/// list of explicit supertrait bounds to render nicely.
|
||||
fn separate_supertrait_bounds(
|
||||
mut g: clean::Generics,
|
||||
) -> (clean::Generics, Vec<clean::GenericBound>) {
|
||||
fn separate_self_bounds(mut g: clean::Generics) -> (clean::Generics, Vec<clean::GenericBound>) {
|
||||
let mut ty_bounds = Vec::new();
|
||||
g.where_predicates.retain(|pred| match *pred {
|
||||
clean::WherePredicate::BoundPredicate { ty: clean::SelfTy, ref bounds, .. } => {
|
||||
|
|
@ -806,22 +812,17 @@ fn separate_supertrait_bounds(
|
|||
}
|
||||
|
||||
pub(crate) fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) {
|
||||
if did.is_local() {
|
||||
if did.is_local()
|
||||
|| cx.external_traits.contains_key(&did)
|
||||
|| cx.active_extern_traits.contains(&did)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
if cx.external_traits.contains_key(&did) || cx.active_extern_traits.contains(&did) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
cx.active_extern_traits.insert(did);
|
||||
}
|
||||
cx.active_extern_traits.insert(did);
|
||||
|
||||
debug!("record_extern_trait: {did:?}");
|
||||
let trait_ = build_external_trait(cx, did);
|
||||
let trait_ = build_trait(cx, did);
|
||||
|
||||
cx.external_traits.insert(did, trait_);
|
||||
cx.active_extern_traits.remove(&did);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use rustc_span::source_map;
|
|||
use rustc_span::symbol::sym;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::clean::inline::build_external_trait;
|
||||
use crate::clean::inline::build_trait;
|
||||
use crate::clean::{self, ItemId};
|
||||
use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions};
|
||||
use crate::formats::cache::Cache;
|
||||
|
|
@ -385,7 +385,7 @@ pub(crate) fn run_global_ctxt(
|
|||
//
|
||||
// Note that in case of `#![no_core]`, the trait is not available.
|
||||
if let Some(sized_trait_did) = ctxt.tcx.lang_items().sized_trait() {
|
||||
let sized_trait = build_external_trait(&mut ctxt, sized_trait_did);
|
||||
let sized_trait = build_trait(&mut ctxt, sized_trait_did);
|
||||
ctxt.external_traits.insert(sized_trait_did, sized_trait);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1232,12 +1232,13 @@ fn item_trait_alias(
|
|||
wrap_item(w, |w| {
|
||||
write!(
|
||||
w,
|
||||
"{attrs}trait {name}{generics}{where_b} = {bounds};",
|
||||
"{attrs}trait {name}{generics} = {bounds}{where_clause};",
|
||||
attrs = render_attributes_in_pre(it, "", cx),
|
||||
name = it.name.unwrap(),
|
||||
generics = t.generics.print(cx),
|
||||
where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(),
|
||||
bounds = bounds(&t.bounds, true, cx),
|
||||
where_clause =
|
||||
print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(),
|
||||
)
|
||||
})?;
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
|||
"ignore-arm-unknown-linux-gnueabihf",
|
||||
"ignore-arm-unknown-linux-musleabi",
|
||||
"ignore-arm-unknown-linux-musleabihf",
|
||||
"ignore-auxiliary",
|
||||
"ignore-avr",
|
||||
"ignore-beta",
|
||||
"ignore-cdb",
|
||||
|
|
|
|||
|
|
@ -100,6 +100,10 @@ fn parse_cfg_name_directive<'a>(
|
|||
name: "test",
|
||||
message: "always"
|
||||
}
|
||||
condition! {
|
||||
name: "auxiliary",
|
||||
message: "used by another main test file"
|
||||
}
|
||||
condition! {
|
||||
name: &config.target,
|
||||
allowed_names: &target_cfgs.all_targets,
|
||||
|
|
|
|||
|
|
@ -940,3 +940,9 @@ fn test_supported_crate_types() {
|
|||
"//@ needs-crate-type: bin, cdylib, dylib, lib, proc-macro, rlib, staticlib"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ignore_auxiliary() {
|
||||
let config = cfg().build();
|
||||
assert!(check_ignore(&config, "//@ ignore-auxiliary"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use std::process::{Child, Command, ExitStatus, Output, Stdio};
|
|||
use std::sync::Arc;
|
||||
use std::{env, iter, str};
|
||||
|
||||
use build_helper::fs::remove_and_create_dir_all;
|
||||
use camino::{Utf8Path, Utf8PathBuf};
|
||||
use colored::Colorize;
|
||||
use regex::{Captures, Regex};
|
||||
|
|
@ -207,12 +208,6 @@ pub fn compute_stamp_hash(config: &Config) -> String {
|
|||
format!("{:x}", hash.finish())
|
||||
}
|
||||
|
||||
fn remove_and_create_dir_all(path: &Utf8Path) {
|
||||
let path = path.as_std_path();
|
||||
let _ = fs::remove_dir_all(path);
|
||||
fs::create_dir_all(path).unwrap();
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct TestCx<'test> {
|
||||
config: &'test Config,
|
||||
|
|
@ -523,7 +518,9 @@ impl<'test> TestCx<'test> {
|
|||
let mut rustc = Command::new(&self.config.rustc_path);
|
||||
|
||||
let out_dir = self.output_base_name().with_extension("pretty-out");
|
||||
remove_and_create_dir_all(&out_dir);
|
||||
remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| {
|
||||
panic!("failed to remove and recreate output directory `{out_dir}`: {e}")
|
||||
});
|
||||
|
||||
let target = if self.props.force_host { &*self.config.host } else { &*self.config.target };
|
||||
|
||||
|
|
@ -1098,13 +1095,19 @@ impl<'test> TestCx<'test> {
|
|||
let aux_dir = self.aux_output_dir_name();
|
||||
|
||||
if !self.props.aux.builds.is_empty() {
|
||||
remove_and_create_dir_all(&aux_dir);
|
||||
remove_and_create_dir_all(&aux_dir).unwrap_or_else(|e| {
|
||||
panic!("failed to remove and recreate output directory `{aux_dir}`: {e}")
|
||||
});
|
||||
}
|
||||
|
||||
if !self.props.aux.bins.is_empty() {
|
||||
let aux_bin_dir = self.aux_bin_output_dir_name();
|
||||
remove_and_create_dir_all(&aux_dir);
|
||||
remove_and_create_dir_all(&aux_bin_dir);
|
||||
remove_and_create_dir_all(&aux_dir).unwrap_or_else(|e| {
|
||||
panic!("failed to remove and recreate output directory `{aux_dir}`: {e}")
|
||||
});
|
||||
remove_and_create_dir_all(&aux_bin_dir).unwrap_or_else(|e| {
|
||||
panic!("failed to remove and recreate output directory `{aux_bin_dir}`: {e}")
|
||||
});
|
||||
}
|
||||
|
||||
aux_dir
|
||||
|
|
@ -1509,7 +1512,9 @@ impl<'test> TestCx<'test> {
|
|||
|
||||
let set_mir_dump_dir = |rustc: &mut Command| {
|
||||
let mir_dump_dir = self.get_mir_dump_dir();
|
||||
remove_and_create_dir_all(&mir_dump_dir);
|
||||
remove_and_create_dir_all(&mir_dump_dir).unwrap_or_else(|e| {
|
||||
panic!("failed to remove and recreate output directory `{mir_dump_dir}`: {e}")
|
||||
});
|
||||
let mut dir_opt = "-Zdump-mir-dir=".to_string();
|
||||
dir_opt.push_str(mir_dump_dir.as_str());
|
||||
debug!("dir_opt: {:?}", dir_opt);
|
||||
|
|
@ -1969,7 +1974,9 @@ impl<'test> TestCx<'test> {
|
|||
let suffix =
|
||||
self.safe_revision().map_or("nightly".into(), |path| path.to_owned() + "-nightly");
|
||||
let compare_dir = output_base_dir(self.config, self.testpaths, Some(&suffix));
|
||||
remove_and_create_dir_all(&compare_dir);
|
||||
remove_and_create_dir_all(&compare_dir).unwrap_or_else(|e| {
|
||||
panic!("failed to remove and recreate output directory `{compare_dir}`: {e}")
|
||||
});
|
||||
|
||||
// We need to create a new struct for the lifetimes on `config` to work.
|
||||
let new_rustdoc = TestCx {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ impl TestCx<'_> {
|
|||
assert!(self.revision.is_none(), "revisions not relevant here");
|
||||
|
||||
let out_dir = self.output_base_dir();
|
||||
remove_and_create_dir_all(&out_dir);
|
||||
remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| {
|
||||
panic!("failed to remove and recreate output directory `{out_dir}`: {e}")
|
||||
});
|
||||
|
||||
let proc_res = self.document(&out_dir, &self.testpaths);
|
||||
if !proc_res.status.success() {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ impl TestCx<'_> {
|
|||
assert!(self.revision.is_none(), "revisions not relevant here");
|
||||
|
||||
let out_dir = self.output_base_dir();
|
||||
remove_and_create_dir_all(&out_dir);
|
||||
remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| {
|
||||
panic!("failed to remove and recreate output directory `{out_dir}`: {e}")
|
||||
});
|
||||
|
||||
let proc_res = self.document(&out_dir, &self.testpaths);
|
||||
if !proc_res.status.success() {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ pub fn main() {
|
|||
TLS.set(Some(Box::leak(Box::new(123))));
|
||||
|
||||
// We can only ignore leaks on targets that use `#[thread_local]` statics to implement
|
||||
// `thread_local!`. Ignore the test on targest that don't.
|
||||
// `thread_local!`. Ignore the test on targets that don't.
|
||||
if cfg!(target_thread_local) {
|
||||
thread_local! {
|
||||
static TLS_KEY: Cell<Option<&'static i32>> = Cell::new(None);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ pub struct Environment {
|
|||
prebuilt_rustc_perf: Option<Utf8PathBuf>,
|
||||
use_bolt: bool,
|
||||
shared_llvm: bool,
|
||||
run_tests: bool,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
|
|
@ -101,6 +102,10 @@ impl Environment {
|
|||
pub fn benchmark_cargo_config(&self) -> &[String] {
|
||||
&self.benchmark_cargo_config
|
||||
}
|
||||
|
||||
pub fn run_tests(&self) -> bool {
|
||||
self.run_tests
|
||||
}
|
||||
}
|
||||
|
||||
/// What is the extension of binary executables on this platform?
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ enum EnvironmentCmd {
|
|||
/// Arguments passed to `rustc-perf --cargo-config <value>` when running benchmarks.
|
||||
#[arg(long)]
|
||||
benchmark_cargo_config: Vec<String>,
|
||||
|
||||
/// Perform tests after final build if it's not a try build
|
||||
#[arg(long)]
|
||||
run_tests: bool,
|
||||
},
|
||||
/// Perform an optimized build on Linux CI, from inside Docker.
|
||||
LinuxCi {
|
||||
|
|
@ -125,6 +129,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
|
|||
skipped_tests,
|
||||
benchmark_cargo_config,
|
||||
shared,
|
||||
run_tests,
|
||||
} => {
|
||||
let env = EnvironmentBuilder::default()
|
||||
.host_tuple(target_triple)
|
||||
|
|
@ -138,6 +143,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
|
|||
.use_bolt(use_bolt)
|
||||
.skipped_tests(skipped_tests)
|
||||
.benchmark_cargo_config(benchmark_cargo_config)
|
||||
.run_tests(run_tests)
|
||||
.build()?;
|
||||
|
||||
(env, shared.build_args)
|
||||
|
|
@ -160,6 +166,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
|
|||
// FIXME: Enable bolt for aarch64 once it's fixed upstream. Broken as of December 2024.
|
||||
.use_bolt(!is_aarch64)
|
||||
.skipped_tests(vec![])
|
||||
.run_tests(true)
|
||||
.build()?;
|
||||
|
||||
(env, shared.build_args)
|
||||
|
|
@ -179,6 +186,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
|
|||
.shared_llvm(false)
|
||||
.use_bolt(false)
|
||||
.skipped_tests(vec![])
|
||||
.run_tests(true)
|
||||
.build()?;
|
||||
|
||||
(env, shared.build_args)
|
||||
|
|
@ -344,7 +352,7 @@ fn execute_pipeline(
|
|||
// possible regressions.
|
||||
// The tests are not executed for try builds, which can be in various broken states, so we don't
|
||||
// want to gatekeep them with tests.
|
||||
if !is_try_build() {
|
||||
if !is_try_build() && env.run_tests() {
|
||||
timer.section("Run tests", |_| run_tests(env))?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,5 @@ build_helper = { path = "../../build_helper" }
|
|||
serde_json = "1.0"
|
||||
libc = "0.2"
|
||||
|
||||
# FIXME(#137532): replace `os_pipe` with `anonymous_pipe` once it stabilizes and
|
||||
# reaches beta.
|
||||
os_pipe = "1.2.1"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "dylib"]
|
||||
|
|
|
|||
|
|
@ -41,8 +41,6 @@ pub use bstr;
|
|||
pub use gimli;
|
||||
pub use libc;
|
||||
pub use object;
|
||||
// FIXME(#137532): replace with std `anonymous_pipe` once it stabilizes and reaches beta.
|
||||
pub use os_pipe;
|
||||
pub use regex;
|
||||
pub use serde_json;
|
||||
pub use similar;
|
||||
|
|
|
|||
|
|
@ -902,9 +902,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
|
|
|
|||
|
|
@ -260,7 +260,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
|
|||
"constant_time_eq",
|
||||
"cpufeatures",
|
||||
"crc32fast",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
|
|
@ -295,7 +294,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
|
|||
"gimli",
|
||||
"gsgdt",
|
||||
"hashbrown",
|
||||
"hermit-abi",
|
||||
"icu_list",
|
||||
"icu_list_data",
|
||||
"icu_locid",
|
||||
|
|
@ -329,7 +327,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
|
|||
"miniz_oxide",
|
||||
"nix",
|
||||
"nu-ansi-term",
|
||||
"num_cpus",
|
||||
"object",
|
||||
"odht",
|
||||
"once_cell",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
// MIPS assembler uses the label prefix `$anon.` for local anonymous variables
|
||||
// other architectures (including ARM and x86-64) use the prefix `.Lanon.`
|
||||
//@ only-linux
|
||||
//@ assembly-output: emit-asm
|
||||
//@ compile-flags: --crate-type=lib -Copt-level=3
|
||||
|
|
@ -6,13 +8,13 @@
|
|||
use std::ffi::CStr;
|
||||
|
||||
// CHECK: .section .rodata.str1.{{[12]}},"aMS"
|
||||
// CHECK: .Lanon.{{.+}}:
|
||||
// CHECK: {{(\.L|\$)}}anon.{{.+}}:
|
||||
// CHECK-NEXT: .asciz "foo"
|
||||
#[unsafe(no_mangle)]
|
||||
static CSTR: &[u8; 4] = b"foo\0";
|
||||
|
||||
// CHECK-NOT: .section
|
||||
// CHECK: .Lanon.{{.+}}:
|
||||
// CHECK: {{(\.L|\$)}}anon.{{.+}}:
|
||||
// CHECK-NEXT: .asciz "bar"
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn cstr() -> &'static CStr {
|
||||
|
|
@ -20,7 +22,7 @@ pub fn cstr() -> &'static CStr {
|
|||
}
|
||||
|
||||
// CHECK-NOT: .section
|
||||
// CHECK: .Lanon.{{.+}}:
|
||||
// CHECK: {{(\.L|\$)}}anon.{{.+}}:
|
||||
// CHECK-NEXT: .asciz "baz"
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn manual_cstr() -> &'static str {
|
||||
|
|
|
|||
113
tests/codegen/autodiffv2.rs
Normal file
113
tests/codegen/autodiffv2.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat
|
||||
//@ no-prefer-dynamic
|
||||
//@ needs-enzyme
|
||||
//
|
||||
// In Enzyme, we test against a large range of LLVM versions (5+) and don't have overly many
|
||||
// breakages. One benefit is that we match the IR generated by Enzyme only after running it
|
||||
// through LLVM's O3 pipeline, which will remove most of the noise.
|
||||
// However, our integration test could also be affected by changes in how rustc lowers MIR into
|
||||
// LLVM-IR, which could cause additional noise and thus breakages. If that's the case, we should
|
||||
// reduce this test to only match the first lines and the ret instructions.
|
||||
//
|
||||
// The function tested here has 4 inputs and 5 outputs, so we could either call forward-mode
|
||||
// autodiff 4 times, or reverse mode 5 times. Since a forward-mode call is usually faster than
|
||||
// reverse mode, we prefer it here. This file also tests a new optimization (batch mode), which
|
||||
// allows us to call forward-mode autodiff only once, and get all 5 outputs in a single call.
|
||||
//
|
||||
// We support 2 different batch modes. `d_square2` has the same interface as scalar forward-mode,
|
||||
// but each shadow argument is `width` times larger (thus 16 and 20 elements here).
|
||||
// `d_square3` instead takes `width` (4) shadow arguments, which are all the same size as the
|
||||
// original function arguments.
|
||||
//
|
||||
// FIXME(autodiff): We currently can't test `d_square1` and `d_square3` in the same file, since they
|
||||
// generate the same dummy functions which get merged by LLVM, breaking pieces of our pipeline which
|
||||
// try to rewrite the dummy functions later. We should consider to change to pure declarations both
|
||||
// in our frontend and in the llvm backend to avoid these issues.
|
||||
|
||||
#![feature(autodiff)]
|
||||
|
||||
use std::autodiff::autodiff;
|
||||
|
||||
#[no_mangle]
|
||||
//#[autodiff(d_square1, Forward, Dual, Dual)]
|
||||
#[autodiff(d_square2, Forward, 4, Dualv, Dualv)]
|
||||
#[autodiff(d_square3, Forward, 4, Dual, Dual)]
|
||||
fn square(x: &[f32], y: &mut [f32]) {
|
||||
assert!(x.len() >= 4);
|
||||
assert!(y.len() >= 5);
|
||||
y[0] = 4.3 * x[0] + 1.2 * x[1] + 3.4 * x[2] + 2.1 * x[3];
|
||||
y[1] = 2.3 * x[0] + 4.5 * x[1] + 1.7 * x[2] + 6.4 * x[3];
|
||||
y[2] = 1.1 * x[0] + 3.3 * x[1] + 2.5 * x[2] + 4.7 * x[3];
|
||||
y[3] = 5.2 * x[0] + 1.4 * x[1] + 2.6 * x[2] + 3.8 * x[3];
|
||||
y[4] = 1.0 * x[0] + 2.0 * x[1] + 3.0 * x[2] + 4.0 * x[3];
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x1 = std::hint::black_box(vec![0.0, 1.0, 2.0, 3.0]);
|
||||
|
||||
let dx1 = std::hint::black_box(vec![1.0; 12]);
|
||||
|
||||
let z1 = std::hint::black_box(vec![1.0, 0.0, 0.0, 0.0]);
|
||||
let z2 = std::hint::black_box(vec![0.0, 1.0, 0.0, 0.0]);
|
||||
let z3 = std::hint::black_box(vec![0.0, 0.0, 1.0, 0.0]);
|
||||
let z4 = std::hint::black_box(vec![0.0, 0.0, 0.0, 1.0]);
|
||||
|
||||
let z5 = std::hint::black_box(vec![
|
||||
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
|
||||
]);
|
||||
|
||||
let mut y1 = std::hint::black_box(vec![0.0; 5]);
|
||||
let mut y2 = std::hint::black_box(vec![0.0; 5]);
|
||||
let mut y3 = std::hint::black_box(vec![0.0; 5]);
|
||||
let mut y4 = std::hint::black_box(vec![0.0; 5]);
|
||||
|
||||
let mut y5 = std::hint::black_box(vec![0.0; 5]);
|
||||
|
||||
let mut y6 = std::hint::black_box(vec![0.0; 5]);
|
||||
|
||||
let mut dy1_1 = std::hint::black_box(vec![0.0; 5]);
|
||||
let mut dy1_2 = std::hint::black_box(vec![0.0; 5]);
|
||||
let mut dy1_3 = std::hint::black_box(vec![0.0; 5]);
|
||||
let mut dy1_4 = std::hint::black_box(vec![0.0; 5]);
|
||||
|
||||
let mut dy2 = std::hint::black_box(vec![0.0; 20]);
|
||||
|
||||
let mut dy3_1 = std::hint::black_box(vec![0.0; 5]);
|
||||
let mut dy3_2 = std::hint::black_box(vec![0.0; 5]);
|
||||
let mut dy3_3 = std::hint::black_box(vec![0.0; 5]);
|
||||
let mut dy3_4 = std::hint::black_box(vec![0.0; 5]);
|
||||
|
||||
// scalar.
|
||||
//d_square1(&x1, &z1, &mut y1, &mut dy1_1);
|
||||
//d_square1(&x1, &z2, &mut y2, &mut dy1_2);
|
||||
//d_square1(&x1, &z3, &mut y3, &mut dy1_3);
|
||||
//d_square1(&x1, &z4, &mut y4, &mut dy1_4);
|
||||
|
||||
// assert y1 == y2 == y3 == y4
|
||||
//for i in 0..5 {
|
||||
// assert_eq!(y1[i], y2[i]);
|
||||
// assert_eq!(y1[i], y3[i]);
|
||||
// assert_eq!(y1[i], y4[i]);
|
||||
//}
|
||||
|
||||
// batch mode A)
|
||||
d_square2(&x1, &z5, &mut y5, &mut dy2);
|
||||
|
||||
// assert y1 == y2 == y3 == y4 == y5
|
||||
//for i in 0..5 {
|
||||
// assert_eq!(y1[i], y5[i]);
|
||||
//}
|
||||
|
||||
// batch mode B)
|
||||
d_square3(&x1, &z1, &z2, &z3, &z4, &mut y6, &mut dy3_1, &mut dy3_2, &mut dy3_3, &mut dy3_4);
|
||||
for i in 0..5 {
|
||||
assert_eq!(y5[i], y6[i]);
|
||||
}
|
||||
|
||||
for i in 0..5 {
|
||||
assert_eq!(dy2[0..5][i], dy3_1[i]);
|
||||
assert_eq!(dy2[5..10][i], dy3_2[i]);
|
||||
assert_eq!(dy2[10..15][i], dy3_3[i]);
|
||||
assert_eq!(dy2[15..20][i], dy3_4[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
// at the time still sometimes fails, so only verify it for the power-of-two size
|
||||
// - https://github.com/llvm/llvm-project/issues/134735
|
||||
//@[x86-64-v3] only-x86_64
|
||||
//@[x86-64-v3] min-llvm-version: 21
|
||||
//@[x86-64-v3] compile-flags: -Ctarget-cpu=x86-64-v3
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
|
@ -19,16 +20,7 @@
|
|||
#[no_mangle]
|
||||
pub fn test() -> usize {
|
||||
// CHECK-LABEL: @test(
|
||||
// host: ret {{i64|i32}} 165
|
||||
// x86-64: ret {{i64|i32}} 165
|
||||
|
||||
// FIXME: Now that this autovectorizes via a masked load, it doesn't actually
|
||||
// const-fold for certain widths. The `test_eight` case below shows that, yes,
|
||||
// what we're emitting *can* be const-folded, except that the way LLVM does it
|
||||
// for certain widths doesn't today. We should be able to put this back to
|
||||
// the same check after <https://github.com/llvm/llvm-project/issues/134513>
|
||||
// x86-64-v3: masked.load
|
||||
|
||||
// CHECK: ret {{i64|i32}} 165
|
||||
let values = [23, 16, 54, 3, 60, 9];
|
||||
let mut acc = 0;
|
||||
for item in values {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//@ ignore-test: this is not a test
|
||||
//@ ignore-auxiliary (used by `./main.rs`)
|
||||
|
||||
#[inline]
|
||||
pub fn some_aux_mod_function() -> i32 {
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
//@ known-bug: #100618
|
||||
//@ compile-flags: -Cdebuginfo=2
|
||||
|
||||
//@ only-x86_64
|
||||
enum Foo<T: 'static> {
|
||||
Value(T),
|
||||
Recursive(&'static Foo<Option<T>>),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _x = Foo::Value(());
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
//@ known-bug: #115994
|
||||
//@ compile-flags: -Cdebuginfo=2 --crate-type lib
|
||||
|
||||
// To prevent "overflow while adding drop-check rules".
|
||||
use std::mem::ManuallyDrop;
|
||||
|
||||
pub enum Foo<U> {
|
||||
Leaf(U),
|
||||
|
||||
Branch(BoxedFoo<BoxedFoo<U>>),
|
||||
}
|
||||
|
||||
pub type BoxedFoo<U> = ManuallyDrop<Box<Foo<U>>>;
|
||||
|
||||
pub fn test() -> Foo<usize> {
|
||||
todo!()
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
//@ known-bug: #121538
|
||||
//@ compile-flags: -Cdebuginfo=2
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
struct Digit<T> {
|
||||
elem: T
|
||||
}
|
||||
|
||||
struct Node<T:'static> { m: PhantomData<&'static T> }
|
||||
|
||||
enum FingerTree<T:'static> {
|
||||
Single(T),
|
||||
|
||||
Deep(
|
||||
Digit<T>,
|
||||
Node<FingerTree<Node<T>>>,
|
||||
)
|
||||
}
|
||||
|
||||
enum Wrapper<T:'static> {
|
||||
Simple,
|
||||
Other(FingerTree<T>),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let w =
|
||||
Some(Wrapper::Simple::<u32>);
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
//@ ignore-android
|
||||
//@ ignore-test: #128971
|
||||
|
||||
// FIXME: stepping with "next" in a debugger skips past end-of-scope drops
|
||||
//@ ignore-test (broken, see #128971)
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
// gdb-command:run
|
||||
|
||||
// Test whether compiling a recursive enum definition crashes debug info generation. The test case
|
||||
// is taken from issue #11083.
|
||||
// is taken from issue #11083 and #135093.
|
||||
|
||||
#![allow(unused_variables)]
|
||||
#![feature(omit_gdb_pretty_printer_section)]
|
||||
|
|
@ -18,6 +18,21 @@ struct WindowCallbacks<'a> {
|
|||
pos_callback: Option<Box<FnMut(&Window, i32, i32) + 'a>>,
|
||||
}
|
||||
|
||||
enum ExpandingRecursive<T> {
|
||||
Recurse(Indirect<T>),
|
||||
Item(T),
|
||||
}
|
||||
|
||||
struct Indirect<U> {
|
||||
rec: *const ExpandingRecursive<Option<U>>,
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
let x = WindowCallbacks { pos_callback: None };
|
||||
|
||||
// EXPANDING RECURSIVE
|
||||
let expanding_recursive: ExpandingRecursive<u64> = ExpandingRecursive::Recurse(Indirect {
|
||||
rec: &ExpandingRecursive::Item(Option::Some(42)),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
//@ known-bug: #107362
|
||||
//@ compile-flags: -Cdebuginfo=2
|
||||
|
||||
pub trait Functor
|
||||
|
|
@ -14,9 +14,7 @@
|
|||
use std::io::Read;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
// FIXME(#137532): replace `os_pipe` dependency with std `anonymous_pipe` once that stabilizes and
|
||||
// reaches beta.
|
||||
use run_make_support::{env_var, os_pipe};
|
||||
use run_make_support::env_var;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Binary {
|
||||
|
|
@ -25,7 +23,7 @@ enum Binary {
|
|||
}
|
||||
|
||||
fn check_broken_pipe_handled_gracefully(bin: Binary, mut cmd: Command) {
|
||||
let (reader, writer) = os_pipe::pipe().unwrap();
|
||||
let (reader, writer) = std::io::pipe().unwrap();
|
||||
drop(reader); // close read-end
|
||||
cmd.stdout(writer).stderr(Stdio::piped());
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
@@ -1,5 +1,5 @@
|
||||
error: unknown print request: `xxx`
|
||||
|
|
||||
- = help: valid print requests are: `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `tls-models`
|
||||
+ = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models`
|
||||
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information
|
||||
|
||||
33
tests/run-make/print-request-help-stable-unstable/rmake.rs
Normal file
33
tests/run-make/print-request-help-stable-unstable/rmake.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
//! Check that unstable print requests are omitted from help if compiler is in stable channel.
|
||||
//!
|
||||
//! Issue: <https://github.com/rust-lang/rust/issues/138698>
|
||||
use run_make_support::{diff, rustc, similar};
|
||||
|
||||
fn main() {
|
||||
let stable_invalid_print_request_help = rustc()
|
||||
.env("RUSTC_BOOTSTRAP", "-1")
|
||||
.cfg("force_stable")
|
||||
.print("xxx")
|
||||
.run_fail()
|
||||
.stderr_utf8();
|
||||
assert!(!stable_invalid_print_request_help.contains("all-target-specs-json"));
|
||||
diff()
|
||||
.expected_file("stable-invalid-print-request-help.err")
|
||||
.actual_text("stable_invalid_print_request_help", &stable_invalid_print_request_help)
|
||||
.run();
|
||||
|
||||
let unstable_invalid_print_request_help = rustc().print("xxx").run_fail().stderr_utf8();
|
||||
assert!(unstable_invalid_print_request_help.contains("all-target-specs-json"));
|
||||
diff()
|
||||
.expected_file("unstable-invalid-print-request-help.err")
|
||||
.actual_text("unstable_invalid_print_request_help", &unstable_invalid_print_request_help)
|
||||
.run();
|
||||
|
||||
let help_diff = similar::TextDiff::from_lines(
|
||||
&stable_invalid_print_request_help,
|
||||
&unstable_invalid_print_request_help,
|
||||
)
|
||||
.unified_diff()
|
||||
.to_string();
|
||||
diff().expected_file("help-diff.diff").actual_text("help_diff", help_diff).run();
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
error: unknown print request: `xxx`
|
||||
|
|
||||
= help: valid print requests are: `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `tls-models`
|
||||
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
error: unknown print request: `xxx`
|
||||
|
|
||||
= help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models`
|
||||
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information
|
||||
|
||||
13
tests/rustdoc/auxiliary/ext-trait-aliases.rs
Normal file
13
tests/rustdoc/auxiliary/ext-trait-aliases.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#![feature(trait_alias)]
|
||||
|
||||
pub trait ExtAlias0 = Copy + Iterator<Item = u8>;
|
||||
|
||||
pub trait ExtAlias1<'a, T: 'a + Clone, const N: usize> = From<[&'a T; N]>;
|
||||
|
||||
pub trait ExtAlias2<T> = where T: From<String>, String: Into<T>;
|
||||
|
||||
pub trait ExtAlias3 = Sized;
|
||||
|
||||
pub trait ExtAlias4 = where Self: Sized;
|
||||
|
||||
pub trait ExtAlias5 = ;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#![feature(trait_alias)]
|
||||
|
||||
pub trait SomeAlias = std::fmt::Debug + std::marker::Copy;
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
//@ aux-build:trait-alias-mention.rs
|
||||
//@ build-aux-docs
|
||||
|
||||
#![crate_name = "foo"]
|
||||
|
||||
extern crate trait_alias_mention;
|
||||
|
||||
//@ has foo/fn.mention_alias_in_bounds.html '//a[@href="../trait_alias_mention/traitalias.SomeAlias.html"]' 'SomeAlias'
|
||||
pub fn mention_alias_in_bounds<T: trait_alias_mention::SomeAlias>() {
|
||||
}
|
||||
82
tests/rustdoc/trait-aliases.rs
Normal file
82
tests/rustdoc/trait-aliases.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
// Basic testing for trait aliases.
|
||||
#![feature(trait_alias)]
|
||||
#![crate_name = "it"]
|
||||
|
||||
// Check the "local case" (HIR cleaning) //
|
||||
|
||||
//@ has it/all.html '//a[@href="traitalias.Alias0.html"]' 'Alias0'
|
||||
//@ has it/index.html '//h2[@id="trait-aliases"]' 'Trait Aliases'
|
||||
//@ has it/index.html '//a[@class="traitalias"]' 'Alias0'
|
||||
//@ has it/traitalias.Alias0.html
|
||||
//@ has - '//*[@class="rust item-decl"]//code' 'trait Alias0 = Copy + Iterator<Item = u8>;'
|
||||
pub trait Alias0 = Copy + Iterator<Item = u8>;
|
||||
|
||||
//@ has it/traitalias.Alias1.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' \
|
||||
// "trait Alias1<'a, T: 'a + Clone, const N: usize> = From<[&'a T; N]>;"
|
||||
pub trait Alias1<'a, T: 'a + Clone, const N: usize> = From<[&'a T; N]>;
|
||||
|
||||
//@ has it/traitalias.Alias2.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' \
|
||||
// 'trait Alias2<T> = where T: From<String>, String: Into<T>;'
|
||||
pub trait Alias2<T> = where T: From<String>, String: Into<T>;
|
||||
|
||||
//@ has it/traitalias.Alias3.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' 'trait Alias3 = ;'
|
||||
pub trait Alias3 =;
|
||||
|
||||
//@ has it/traitalias.Alias4.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' 'trait Alias4 = ;'
|
||||
pub trait Alias4 = where;
|
||||
|
||||
//@ has it/fn.usage0.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' "pub fn usage0(_: impl Alias0)"
|
||||
//@ has - '//a[@href="traitalias.Alias0.html"]' 'Alias0'
|
||||
pub fn usage0(_: impl Alias0) {}
|
||||
|
||||
// FIXME: One can only "disambiguate" intra-doc links to trait aliases with `type@` but not with
|
||||
// `trait@` (fails to resolve) or `traitalias@` (doesn't exist). We should make at least one of
|
||||
// the latter two work, right?
|
||||
|
||||
//@ has it/link0/index.html
|
||||
//@ has - '//a/@href' 'traitalias.Alias0.html'
|
||||
//@ has - '//a/@href' 'traitalias.Alias1.html'
|
||||
/// [Alias0], [type@Alias1]
|
||||
pub mod link0 {}
|
||||
|
||||
// Check the "extern case" (middle cleaning) //
|
||||
|
||||
//@ aux-build: ext-trait-aliases.rs
|
||||
extern crate ext_trait_aliases as ext;
|
||||
|
||||
//@ has it/traitalias.ExtAlias0.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias0 = Copy + Iterator<Item = u8>;'
|
||||
pub use ext::ExtAlias0;
|
||||
|
||||
//@ has it/traitalias.ExtAlias1.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' \
|
||||
// "trait ExtAlias1<'a, T, const N: usize> = From<[&'a T; N]> where T: 'a + Clone;"
|
||||
pub use ext::ExtAlias1;
|
||||
|
||||
//@ has it/traitalias.ExtAlias2.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' \
|
||||
// 'trait ExtAlias2<T> = where T: From<String>, String: Into<T>;'
|
||||
pub use ext::ExtAlias2;
|
||||
|
||||
//@ has it/traitalias.ExtAlias3.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias3 = Sized;'
|
||||
pub use ext::ExtAlias3;
|
||||
|
||||
// NOTE: Middle cleaning can't discern `= Sized` and `= where Self: Sized` and that's okay.
|
||||
//@ has it/traitalias.ExtAlias4.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias4 = Sized;'
|
||||
pub use ext::ExtAlias4;
|
||||
|
||||
//@ has it/traitalias.ExtAlias5.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias5 = ;'
|
||||
pub use ext::ExtAlias5;
|
||||
|
||||
//@ has it/fn.usage1.html
|
||||
//@ has - '//pre[@class="rust item-decl"]' "pub fn usage1(_: impl ExtAlias0)"
|
||||
//@ has - '//a[@href="traitalias.ExtAlias0.html"]' 'ExtAlias0'
|
||||
pub fn usage1(_: impl ExtAlias0) {}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#![feature(trait_alias)]
|
||||
|
||||
#![crate_name = "foo"]
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
//@ has foo/all.html '//a[@href="traitalias.CopyAlias.html"]' 'CopyAlias'
|
||||
//@ has foo/all.html '//a[@href="traitalias.Alias2.html"]' 'Alias2'
|
||||
//@ has foo/all.html '//a[@href="traitalias.Foo.html"]' 'Foo'
|
||||
|
||||
//@ has foo/index.html '//h2[@id="trait-aliases"]' 'Trait Aliases'
|
||||
//@ has foo/index.html '//a[@class="traitalias"]' 'CopyAlias'
|
||||
//@ has foo/index.html '//a[@class="traitalias"]' 'Alias2'
|
||||
//@ has foo/index.html '//a[@class="traitalias"]' 'Foo'
|
||||
|
||||
//@ has foo/traitalias.CopyAlias.html
|
||||
//@ has - '//section[@id="main-content"]/pre[@class="rust item-decl"]' 'trait CopyAlias = Copy;'
|
||||
pub trait CopyAlias = Copy;
|
||||
//@ has foo/traitalias.Alias2.html
|
||||
//@ has - '//section[@id="main-content"]/pre[@class="rust item-decl"]' 'trait Alias2 = Copy + Debug;'
|
||||
pub trait Alias2 = Copy + Debug;
|
||||
//@ has foo/traitalias.Foo.html
|
||||
//@ has - '//section[@id="main-content"]/pre[@class="rust item-decl"]' 'trait Foo<T> = Into<T> + Debug;'
|
||||
pub trait Foo<T> = Into<T> + Debug;
|
||||
//@ has foo/fn.bar.html '//a[@href="traitalias.Alias2.html"]' 'Alias2'
|
||||
pub fn bar<T>() where T: Alias2 {}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
//@ ignore-test (auxiliary, used by other tests)
|
||||
//@ ignore-auxiliary (used by `./move-error-snippets.rs`)
|
||||
|
||||
macro_rules! aaa {
|
||||
($c:ident) => {{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//@ ignore-test (auxiliary, used by other tests)
|
||||
//@ ignore-auxiliary (used by `./two_files.rs`)
|
||||
|
||||
trait Foo { }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
//@ ignore-test (auxiliary, used by other tests)
|
||||
//@ ignore-auxiliary (used by `./inner-cfg-non-inline-mod.rs`)
|
||||
|
||||
#![cfg_attr(all(), cfg(false))]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//@ ignore-test (auxiliary, used by other tests)
|
||||
//@ ignore-auxiliary (used by `./main.rs`)
|
||||
#![crate_type = "lib"]
|
||||
|
||||
macro_rules! underscore {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
//@ ignore-test not a test, auxiliary
|
||||
//@ ignore-auxiliary (used by `../../macro-expanded-mod.rs`)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
//@ ignore-test not a test, auxiliary
|
||||
//@ ignore-auxiliary (used by `../../macro-expanded-mod.rs`)
|
||||
|
||||
mod_decl!(bar);
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
//@ ignore-test this is not a test
|
||||
|
||||
macro_rules! m {
|
||||
() => { mod mod_file_not_owning_aux2; }
|
||||
}
|
||||
m!();
|
||||
|
|
@ -1 +0,0 @@
|
|||
//@ ignore-test this is not a test
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
//@ ignore-test this is not a test
|
||||
|
||||
mod mod_file_not_owning_aux2;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
//@ ignore-test auxiliary file for expansion-time.rs
|
||||
//@ ignore-auxiliary (used by `./expansion-time.rs`)
|
||||
|
||||
1
|
||||
2
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//@ ignore-test: not a test
|
||||
//@ ignore-auxiliary (used by `./root.rs`)
|
||||
|
||||
#[allow(tool::lint)]
|
||||
pub fn foo() {}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
//@ ignore-test: not a test
|
||||
//@ ignore-auxiliary (used by `./lint-pre-expansion-extern-module.rs`)
|
||||
|
||||
pub fn try() {}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
//@ ignore-test (auxiliary)
|
||||
|
||||
// Companion to allow-in-other-module.rs
|
||||
//@ ignore-auxiliary (used by `./allow-in-other-module.rs`)
|
||||
|
||||
// This should not warn.
|
||||
#![allow(not_a_real_lint)]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
//@ add-core-stubs
|
||||
//@ build-fail
|
||||
|
||||
#![feature(no_core)]
|
||||
#![feature(no_core, repr_simd)]
|
||||
#![no_core]
|
||||
#![crate_type = "lib"]
|
||||
#![deny(wasm_c_abi)]
|
||||
|
|
@ -39,3 +39,19 @@ pub fn call_other_fun(x: MyType) {
|
|||
unsafe { other_fun(x) } //~ERROR: wasm ABI transition
|
||||
//~^WARN: previously accepted
|
||||
}
|
||||
|
||||
// Zero-sized types are safe in both ABIs
|
||||
#[repr(C)]
|
||||
pub struct MyZstType;
|
||||
#[allow(improper_ctypes_definitions)]
|
||||
pub extern "C" fn zst_safe(_x: (), _y: MyZstType) {}
|
||||
|
||||
// The old and new wasm ABI treats simd types like `v128` the same way, so no
|
||||
// wasm_c_abi warning should be emitted.
|
||||
#[repr(simd)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct v128([i32; 4]);
|
||||
#[target_feature(enable = "simd128")]
|
||||
pub extern "C" fn my_safe_simd(x: v128) -> v128 { x }
|
||||
//~^ WARN `extern` fn uses type `v128`, which is not FFI-safe
|
||||
//~| WARN `extern` fn uses type `v128`, which is not FFI-safe
|
||||
|
|
|
|||
|
|
@ -1,3 +1,32 @@
|
|||
warning: `extern` fn uses type `v128`, which is not FFI-safe
|
||||
--> $DIR/wasm_c_abi_transition.rs:55:35
|
||||
|
|
||||
LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x }
|
||||
| ^^^^ not FFI-safe
|
||||
|
|
||||
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
|
||||
= note: this struct has unspecified layout
|
||||
note: the type is defined here
|
||||
--> $DIR/wasm_c_abi_transition.rs:53:1
|
||||
|
|
||||
LL | pub struct v128([i32; 4]);
|
||||
| ^^^^^^^^^^^^^^^
|
||||
= note: `#[warn(improper_ctypes_definitions)]` on by default
|
||||
|
||||
warning: `extern` fn uses type `v128`, which is not FFI-safe
|
||||
--> $DIR/wasm_c_abi_transition.rs:55:44
|
||||
|
|
||||
LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x }
|
||||
| ^^^^ not FFI-safe
|
||||
|
|
||||
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
|
||||
= note: this struct has unspecified layout
|
||||
note: the type is defined here
|
||||
--> $DIR/wasm_c_abi_transition.rs:53:1
|
||||
|
|
||||
LL | pub struct v128([i32; 4]);
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: this function definition involves an argument of type `MyType` which is affected by the wasm ABI transition
|
||||
--> $DIR/wasm_c_abi_transition.rs:18:1
|
||||
|
|
||||
|
|
@ -33,7 +62,7 @@ LL | unsafe { other_fun(x) }
|
|||
= note: for more information, see issue #138762 <https://github.com/rust-lang/rust/issues/138762>
|
||||
= help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: aborting due to 3 previous errors; 2 warnings emitted
|
||||
|
||||
Future incompatibility report: Future breakage diagnostic:
|
||||
error: this function definition involves an argument of type `MyType` which is affected by the wasm ABI transition
|
||||
|
|
|
|||
|
|
@ -1,3 +1 @@
|
|||
// ignore-test: this is not a test
|
||||
|
||||
1
|
||||
|
|
|
|||
|
|
@ -1,3 +1 @@
|
|||
// ignore-test: this is not a test
|
||||
|
||||
fn foo() { bar() }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//@ ignore-test auxiliary file for include-single-expr.rs
|
||||
//@ ignore-auxiliary (used by `./include-single-expr.rs`)
|
||||
|
||||
0
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//@ ignore-test auxiliary file for include-single-expr.rs
|
||||
//@ ignore-auxiliary (used by `./include-single-expr.rs`)
|
||||
|
||||
0
|
||||
10
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
//@ ignore-test -- this is an auxiliary file as part of another test.
|
||||
//@ ignore-auxiliary (used by `../issue-69838-mods-relative-to-included-path.rs`)
|
||||
|
||||
pub fn i_am_in_bar() {}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue