Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f51fb91
kcfi: only reify trait methods when dyn-compatible
folkertdev Sep 21, 2025
07d41a7
Correctly handle `--no-run` rustdoc test option
GuillaumeGomez Jul 13, 2025
796c4ef
Correctly handle `should_panic` doctest attribute
GuillaumeGomez Jul 7, 2025
11b7070
Add regression test for #143009
GuillaumeGomez Jul 4, 2025
21a4d9d
Update std doctests
GuillaumeGomez Jul 7, 2025
5f2ae4f
Add regression test for #143858
GuillaumeGomez Jul 13, 2025
b001ba6
Add FIXME comments to use `test::ERROR_EXIT_CODE` once public and fix…
GuillaumeGomez Aug 1, 2025
030b664
Use libtest `ERROR_EXIT_CODE` constant
GuillaumeGomez Oct 4, 2025
7e1c00d
Prevent downstream impl DerefMut for Pin<LocalType>
Darksonn Sep 17, 2025
cd44cd0
Improve docs for module
Darksonn Sep 25, 2025
f65250b
Document workaround in docs
Darksonn Sep 29, 2025
76dcb39
Adjust error messages
Darksonn Oct 1, 2025
560d450
Correctly handle `should_panic` on targets not supporting it
GuillaumeGomez Oct 5, 2025
525ed4c
Read the whole test file before parsing directives
Zalathar Oct 7, 2025
a3ba8d8
Use globals instead of metadata, since metadata isn't emitted in debu…
ZuseZ4 Oct 6, 2025
5f9ee17
add incremental/debug test for autodiff
ZuseZ4 Oct 6, 2025
6ce845a
update ui test since the new frontend is a bit more lenient
ZuseZ4 Oct 6, 2025
0fe466c
Fix doc comment
theemathas Oct 7, 2025
43f7eaa
Fix; correct placement of type inference error for method calls
Jamesbarford Oct 6, 2025
3bb883c
Rollup merge of #143900 - GuillaumeGomez:fix-no-run, r=fmease
Zalathar Oct 7, 2025
d467bb7
Rollup merge of #145608 - Darksonn:derefmut-pin-fix, r=lcnr
Zalathar Oct 7, 2025
2d09935
Rollup merge of #146865 - folkertdev:kcfi-only-reify-dyn-compatible, …
Zalathar Oct 7, 2025
73317cb
Rollup merge of #147390 - ZuseZ4:autodiff-dbg, r=jieyouxu
Zalathar Oct 7, 2025
b809e0a
Rollup merge of #147398 - Jamesbarford:fix/method-call-type-inference…
Zalathar Oct 7, 2025
02593be
Rollup merge of #147431 - Zalathar:directive, r=jieyouxu
Zalathar Oct 7, 2025
3688c30
Rollup merge of #147433 - theemathas:theemathas-patch-1, r=chenyukang
Zalathar Oct 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 28 additions & 24 deletions compiler/rustc_codegen_llvm/src/builder/autodiff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::builder::{Builder, PlaceRef, UNNAMED};
use crate::context::SimpleCx;
use crate::declare::declare_simple_fn;
use crate::llvm;
use crate::llvm::{Metadata, TRUE, Type};
use crate::llvm::{TRUE, Type};
use crate::value::Value;

pub(crate) fn adjust_activity_to_abi<'tcx>(
Expand Down Expand Up @@ -159,32 +159,36 @@ fn match_args_from_caller_to_enzyme<'ll, 'tcx>(
let mut outer_pos: usize = 0;
let mut activity_pos = 0;

let enzyme_const = cx.create_metadata(b"enzyme_const");
let enzyme_out = cx.create_metadata(b"enzyme_out");
let enzyme_dup = cx.create_metadata(b"enzyme_dup");
let enzyme_dupv = cx.create_metadata(b"enzyme_dupv");
let enzyme_dupnoneed = cx.create_metadata(b"enzyme_dupnoneed");
let enzyme_dupnoneedv = cx.create_metadata(b"enzyme_dupnoneedv");
// We used to use llvm's metadata to instruct enzyme how to differentiate a function.
// In debug mode we would use incremental compilation which caused the metadata to be
// dropped. This is prevented by now using named globals, which are also understood
// by Enzyme.
let global_const = cx.declare_global("enzyme_const", cx.type_ptr());
let global_out = cx.declare_global("enzyme_out", cx.type_ptr());
let global_dup = cx.declare_global("enzyme_dup", cx.type_ptr());
let global_dupv = cx.declare_global("enzyme_dupv", cx.type_ptr());
let global_dupnoneed = cx.declare_global("enzyme_dupnoneed", cx.type_ptr());
let global_dupnoneedv = cx.declare_global("enzyme_dupnoneedv", cx.type_ptr());

while activity_pos < inputs.len() {
let diff_activity = inputs[activity_pos as usize];
// Duplicated arguments received a shadow argument, into which enzyme will write the
// gradient.
let (activity, duplicated): (&Metadata, bool) = match diff_activity {
let (activity, duplicated): (&llvm::Value, bool) = match diff_activity {
DiffActivity::None => panic!("not a valid input activity"),
DiffActivity::Const => (enzyme_const, false),
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::Const => (global_const, false),
DiffActivity::Active => (global_out, false),
DiffActivity::ActiveOnly => (global_out, false),
DiffActivity::Dual => (global_dup, true),
DiffActivity::Dualv => (global_dupv, true),
DiffActivity::DualOnly => (global_dupnoneed, true),
DiffActivity::DualvOnly => (global_dupnoneedv, true),
DiffActivity::Duplicated => (global_dup, true),
DiffActivity::DuplicatedOnly => (global_dupnoneed, true),
DiffActivity::FakeActivitySize(_) => (global_const, false),
};
let outer_arg = outer_args[outer_pos];
args.push(cx.get_metadata_value(activity));
args.push(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] {
Expand Down Expand Up @@ -244,7 +248,7 @@ fn match_args_from_caller_to_enzyme<'ll, 'tcx>(
assert_eq!(cx.type_kind(next_outer_ty3), TypeKind::Integer);
args.push(next_outer_arg2);
}
args.push(cx.get_metadata_value(enzyme_const));
args.push(global_const);
args.push(next_outer_arg);
outer_pos += 2 + 2 * iterations;
activity_pos += 2;
Expand Down Expand Up @@ -353,13 +357,13 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>(
let mut args = Vec::with_capacity(num_args as usize + 1);
args.push(fn_to_diff);

let enzyme_primal_ret = cx.create_metadata(b"enzyme_primal_return");
let global_primal_ret = cx.declare_global("enzyme_primal_return", cx.type_ptr());
if matches!(attrs.ret_activity, DiffActivity::Dual | DiffActivity::Active) {
args.push(cx.get_metadata_value(enzyme_primal_ret));
args.push(global_primal_ret);
}
if attrs.width > 1 {
let enzyme_width = cx.create_metadata(b"enzyme_width");
args.push(cx.get_metadata_value(enzyme_width));
let global_width = cx.declare_global("enzyme_width", cx.type_ptr());
args.push(global_width);
args.push(cx.get_const_int(cx.type_i64(), attrs.width as u64));
}

Expand Down
21 changes: 16 additions & 5 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sso::SsoHashSet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::HirId;
use rustc_hir::def::DefKind;
use rustc_hir::{self as hir, ExprKind, HirId, Node};
use rustc_hir_analysis::autoderef::{self, Autoderef};
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt};
Expand Down Expand Up @@ -486,13 +485,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty = self.resolve_vars_if_possible(ty.value);
let guar = match *ty.kind() {
ty::Infer(ty::TyVar(_)) => {
// We want to get the variable name that the method
// is being called on. If it is a method call.
let err_span = match (mode, self.tcx.hir_node(scope_expr_id)) {
(
Mode::MethodCall,
Node::Expr(hir::Expr {
kind: ExprKind::MethodCall(_, recv, ..),
..
}),
) => recv.span,
_ => span,
};

let raw_ptr_call = bad_ty.reached_raw_pointer
&& !self.tcx.features().arbitrary_self_types();
// FIXME: Ideally we'd use the span of the self-expr here,
// not of the method path.

let mut err = self.err_ctxt().emit_inference_failure_err(
self.body_id,
span,
err_span,
ty.into(),
TypeAnnotationNeeded::E0282,
!raw_ptr_call,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,7 @@ pub enum PatKind<'tcx> {
/// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are
/// much simpler.
/// * raw pointers derived from integers, other raw pointers will have already resulted in an
// error.
/// error.
/// * `String`, if `string_deref_patterns` is enabled.
Constant {
value: ty::Value<'tcx>,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,11 +618,11 @@ impl<'tcx> Instance<'tcx> {
// be directly reified because it's closure-like. The reify can handle the
// unresolved instance.
resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args }
// Reify `Trait::method` implementations
// FIXME(maurer) only reify it if it is a vtable-safe function
// Reify `Trait::method` implementations if the trait is dyn-compatible.
} else if let Some(assoc) = tcx.opt_associated_item(def_id)
&& let AssocContainer::Trait | AssocContainer::TraitImpl(Ok(_)) =
assoc.container
&& tcx.is_dyn_compatible(assoc.container_id(tcx))
{
// If this function could also go in a vtable, we need to `ReifyShim` it with
// KCFI because it can only attach one type per function.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ symbols! {
PathBuf,
Pending,
PinCoerceUnsized,
PinDerefMutHelper,
Pointer,
Poll,
ProcMacro,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3476,6 +3476,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// can do about it. As far as they are concerned, `?` is compiler magic.
return;
}
if tcx.is_diagnostic_item(sym::PinDerefMutHelper, parent_def_id) {
let parent_predicate =
self.resolve_vars_if_possible(data.derived.parent_trait_pred);

// Skip PinDerefMutHelper in suggestions, but still show downstream suggestions.
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
body_id,
err,
parent_predicate,
param_env,
&data.derived.parent_code,
obligated_types,
seen_requirements,
)
});
return;
}
let self_ty_str =
tcx.short_string(parent_trait_pred.skip_binder().self_ty(), err.long_ty_path());
let trait_name = tcx.short_string(
Expand Down
82 changes: 81 additions & 1 deletion library/core/src/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1689,9 +1689,89 @@ impl<Ptr: [const] Deref> const Deref for Pin<Ptr> {
}
}

mod helper {
/// Helper that prevents downstream crates from implementing `DerefMut` for `Pin`.
///
/// The `Pin` type implements the unsafe trait `PinCoerceUnsized`, which essentially requires
/// that the type does not have a malicious `Deref` or `DerefMut` impl. However, without this
/// helper module, downstream crates are able to write `impl DerefMut for Pin<LocalType>` as
/// long as it does not overlap with the impl provided by stdlib. This is because `Pin` is
/// `#[fundamental]`, so stdlib promises to never implement traits for `Pin` that it does not
/// implement today.
///
/// However, this is problematic. Downstream crates could implement `DerefMut` for
/// `Pin<&LocalType>`, and they could do so maliciously. To prevent this, the implementation for
/// `Pin` delegates to this helper module. Since `helper::Pin` is not `#[fundamental]`, the
/// orphan rules assume that stdlib might implement `helper::DerefMut` for `helper::Pin<&_>` in
/// the future. Because of this, downstream crates can no longer provide an implementation of
/// `DerefMut` for `Pin<&_>`, as it might overlap with a trait impl that, according to the
/// orphan rules, the stdlib could introduce without a breaking change in a future release.
///
/// See <https://github.com/rust-lang/rust/issues/85099> for the issue this fixes.
#[repr(transparent)]
#[unstable(feature = "pin_derefmut_internals", issue = "none")]
#[allow(missing_debug_implementations)]
pub struct PinHelper<Ptr> {
pointer: Ptr,
}

#[unstable(feature = "pin_derefmut_internals", issue = "none")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[rustc_diagnostic_item = "PinDerefMutHelper"]
pub const trait PinDerefMutHelper {
type Target: ?Sized;
fn deref_mut(&mut self) -> &mut Self::Target;
}

#[unstable(feature = "pin_derefmut_internals", issue = "none")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<Ptr: [const] super::DerefMut> const PinDerefMutHelper for PinHelper<Ptr>
where
Ptr::Target: crate::marker::Unpin,
{
type Target = Ptr::Target;

#[inline(always)]
fn deref_mut(&mut self) -> &mut Ptr::Target {
&mut self.pointer
}
}
}

#[stable(feature = "pin", since = "1.33.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<Ptr: [const] DerefMut<Target: Unpin>> const DerefMut for Pin<Ptr> {
#[cfg(not(doc))]
impl<Ptr> const DerefMut for Pin<Ptr>
where
Ptr: [const] Deref,
helper::PinHelper<Ptr>: [const] helper::PinDerefMutHelper<Target = Self::Target>,
{
#[inline]
fn deref_mut(&mut self) -> &mut Ptr::Target {
// SAFETY: Pin and PinHelper have the same layout, so this is equivalent to
// `&mut self.pointer` which is safe because `Target: Unpin`.
helper::PinDerefMutHelper::deref_mut(unsafe {
&mut *(self as *mut Pin<Ptr> as *mut helper::PinHelper<Ptr>)
})
}
}

/// The `Target` type is restricted to `Unpin` types as it's not safe to obtain a mutable reference
/// to a pinned value.
///
/// For soundness reasons, implementations of `DerefMut` for `Pin<T>` are rejected even when `T` is
/// a local type not covered by this impl block. (Since `Pin` is [fundamental], such implementations
/// would normally be possible.)
///
/// [fundamental]: ../../reference/items/implementations.html#r-items.impl.trait.fundamental
#[stable(feature = "pin", since = "1.33.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[cfg(doc)]
impl<Ptr> const DerefMut for Pin<Ptr>
where
Ptr: [const] DerefMut,
<Ptr as Deref>::Target: Unpin,
{
fn deref_mut(&mut self) -> &mut Ptr::Target {
Pin::get_mut(Pin::as_mut(self))
}
Expand Down
16 changes: 12 additions & 4 deletions library/std/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ use crate::fmt::{self, Write};
/// the `Debug` output means `Report` is an ideal starting place for formatting errors returned
/// from `main`.
///
/// ```should_panic
/// ```
/// #![feature(error_reporter)]
/// use std::error::Report;
/// # use std::error::Error;
Expand Down Expand Up @@ -154,10 +154,14 @@ use crate::fmt::{self, Write};
/// # Err(SuperError { source: SuperErrorSideKick })
/// # }
///
/// fn main() -> Result<(), Report<SuperError>> {
/// fn run() -> Result<(), Report<SuperError>> {
/// get_super_error()?;
/// Ok(())
/// }
///
/// fn main() {
/// assert!(run().is_err());
/// }
/// ```
///
/// This example produces the following output:
Expand All @@ -170,7 +174,7 @@ use crate::fmt::{self, Write};
/// output format. If you want to make sure your `Report`s are pretty printed and include backtrace
/// you will need to manually convert and enable those flags.
///
/// ```should_panic
/// ```
/// #![feature(error_reporter)]
/// use std::error::Report;
/// # use std::error::Error;
Expand Down Expand Up @@ -201,12 +205,16 @@ use crate::fmt::{self, Write};
/// # Err(SuperError { source: SuperErrorSideKick })
/// # }
///
/// fn main() -> Result<(), Report<SuperError>> {
/// fn run() -> Result<(), Report<SuperError>> {
/// get_super_error()
/// .map_err(Report::from)
/// .map_err(|r| r.pretty(true).show_backtrace(true))?;
/// Ok(())
/// }
///
/// fn main() {
/// assert!(run().is_err());
/// }
/// ```
///
/// This example produces the following output:
Expand Down
22 changes: 19 additions & 3 deletions src/librustdoc/doctest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ pub(crate) fn run_tests(
);

for (doctest, scraped_test) in &doctests {
tests_runner.add_test(doctest, scraped_test, &target_str);
tests_runner.add_test(doctest, scraped_test, &target_str, rustdoc_options);
}
let (duration, ret) = tests_runner.run_merged_tests(
rustdoc_test_options,
Expand Down Expand Up @@ -801,6 +801,22 @@ fn run_test(
let duration = instant.elapsed();
if doctest.no_run {
return (duration, Ok(()));
} else if doctest.langstr.should_panic
// Equivalent of:
//
// ```
// (cfg!(target_family = "wasm") || cfg!(target_os = "zkvm"))
// && !cfg!(target_os = "emscripten")
// ```
&& let TargetTuple::TargetTuple(ref s) = rustdoc_options.target
&& let mut iter = s.split('-')
&& let Some(arch) = iter.next()
&& iter.next().is_some()
&& let os = iter.next()
&& (arch.starts_with("wasm") || os == Some("zkvm")) && os != Some("emscripten")
{
// We cannot correctly handle `should_panic` in some wasm targets so we exit early.
return (duration, Ok(()));
}

// Run the code!
Expand Down Expand Up @@ -834,7 +850,7 @@ fn run_test(
match result {
Err(e) => return (duration, Err(TestFailure::ExecutionError(e))),
Ok(out) => {
if langstr.should_panic && out.status.success() {
if langstr.should_panic && out.status.code() != Some(test::ERROR_EXIT_CODE) {
return (duration, Err(TestFailure::UnexpectedRunPass));
} else if !langstr.should_panic && !out.status.success() {
return (duration, Err(TestFailure::ExecutionFailure(out)));
Expand Down Expand Up @@ -1144,7 +1160,7 @@ fn doctest_run_fn(
eprint!("Test compiled successfully, but it's marked `compile_fail`.");
}
TestFailure::UnexpectedRunPass => {
eprint!("Test executable succeeded, but it's marked `should_panic`.");
eprint!("Test didn't panic, but it's marked `should_panic`.");
}
TestFailure::MissingErrorCodes(codes) => {
eprint!("Some expected error codes were not found: {codes:?}");
Expand Down
Loading
Loading