diff --git a/RELEASES.md b/RELEASES.md
index 9dfb5c86e3cae..2fa271401fbb3 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,3 +1,12 @@
+Version 1.96.1 (2026-06-30)
+===========================
+
+
+
+- [Cargo: fix timeout/retry behavior](https://github.com/rust-lang/cargo/pull/17131)
+- [Cargo: apply patches for CVE-2025-15661, CVE-2026-55199, and CVE-2026-55200 to libssh2](https://github.com/rust-lang/cargo/pull/17140)
+- [rustc: fix miscompilation in MIR optimization](https://github.com/rust-lang/rust/pull/158214)
+
Version 1.96.0 (2026-05-28)
==========================
diff --git a/compiler/rustc_ast_lowering/src/delegation/attributes.rs b/compiler/rustc_ast_lowering/src/delegation/attributes.rs
new file mode 100644
index 0000000000000..88bd03389c14c
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/delegation/attributes.rs
@@ -0,0 +1,86 @@
+use rustc_hir::attrs::{AttributeKind, InlineAttr};
+use rustc_hir::{self as hir};
+use rustc_span::Span;
+use rustc_span::def_id::DefId;
+
+use crate::LoweringContext;
+use crate::delegation::DelegationResolution;
+
+struct AdditionInfo {
+ pub equals: fn(&hir::Attribute) -> bool,
+ pub kind: AdditionKind,
+}
+
+enum AdditionKind {
+ Default { factory: fn(Span) -> hir::Attribute },
+ Inherit { factory: fn(Span, &hir::Attribute) -> hir::Attribute },
+}
+
+static ADDITIONS: &[AdditionInfo] = &[
+ AdditionInfo {
+ equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::MustUse { .. })),
+ kind: AdditionKind::Inherit {
+ factory: |span, original_attr| {
+ let reason = match original_attr {
+ hir::Attribute::Parsed(AttributeKind::MustUse { reason, .. }) => *reason,
+ _ => None,
+ };
+
+ hir::Attribute::Parsed(AttributeKind::MustUse { span, reason })
+ },
+ },
+ },
+ AdditionInfo {
+ equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::Inline(..))),
+ kind: AdditionKind::Default {
+ factory: |span| hir::Attribute::Parsed(AttributeKind::Inline(InlineAttr::Hint, span)),
+ },
+ },
+];
+
+impl<'hir> LoweringContext<'_, 'hir> {
+ pub(super) fn add_attrs_if_needed(&mut self, resolution: &DelegationResolution) {
+ let &DelegationResolution { span, sig_id, .. } = resolution;
+
+ const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO;
+ let new_attrs = self.create_new_attrs(span, sig_id, self.attrs.get(&PARENT_ID));
+
+ if !new_attrs.is_empty() {
+ let new_attrs = match self.attrs.get(&PARENT_ID) {
+ Some(existing_attrs) => self.arena.alloc_from_iter(
+ existing_attrs.iter().map(|a| a.clone()).chain(new_attrs.into_iter()),
+ ),
+ None => self.arena.alloc_from_iter(new_attrs.into_iter()),
+ };
+
+ self.attrs.insert(PARENT_ID, new_attrs);
+ }
+ }
+
+ fn create_new_attrs(
+ &self,
+ span: Span,
+ sig_id: DefId,
+ existing: Option<&&[hir::Attribute]>,
+ ) -> Vec {
+ ADDITIONS
+ .iter()
+ .filter_map(|addition| {
+ existing
+ .is_none_or(|attrs| !attrs.iter().any(|a| (addition.equals)(a)))
+ .then(|| match addition.kind {
+ AdditionKind::Default { factory } => Some(factory(span)),
+ AdditionKind::Inherit { factory, .. } =>
+ {
+ #[allow(deprecated)]
+ self.tcx
+ .get_all_attrs(sig_id)
+ .iter()
+ .find_map(|a| (addition.equals)(a).then(|| factory(span, a)))
+ }
+ })
+ .flatten()
+ })
+ .collect::>()
+ }
+}
diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs
index bdbaa9c39e21a..cd7b23c18971d 100644
--- a/compiler/rustc_ast_lowering/src/delegation/generics.rs
+++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs
@@ -4,12 +4,13 @@ use rustc_ast::*;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
-use rustc_middle::ty::GenericParamDefKind;
+use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
use rustc_middle::{bug, ty};
use rustc_span::symbol::kw;
use rustc_span::{Ident, Span, sym};
use crate::LoweringContext;
+use crate::delegation::resolution::resolver::DelegationResolver;
use crate::diagnostics::DelegationInfersMismatch;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
@@ -237,74 +238,130 @@ impl<'hir> GenericsGenerationResult<'hir> {
}
}
-impl<'hir> GenericsGenerationResults<'hir> {
- pub(super) fn all_params(&self) -> impl Iterator- > {
- let parent = self.parent.generics.hir_generics_or_empty().params;
- let child = self.child.generics.hir_generics_or_empty().params;
+enum ParentSegmentArgs<'a> {
+ /// Parent segment is valid and generic args are specified:
+ /// `reuse Trait::<'static, ()>::foo;`.
+ Specified(&'a AngleBracketedArgs),
+ /// Parent segment is valid and args are not specified:
+ /// `reuse Trait::foo;`.
+ NotSpecified,
+ /// Parent segment does not exist (`reuse foo`) or we can not
+ /// add generics to it:
+ /// ```rust
+ /// mod to_reuse {
+ /// fn foo() {}
+ /// }
+ ///
+ /// // Can't add generic args to module.
+ /// reuse to_reuse::foo;
+ /// ```
+ Invalid,
+}
- // Order generics, first we have parent and child lifetimes,
- // then parent and child types and consts.
- // `generics_of` in `rustc_hir_analysis` will order them anyway,
- // however we want the order to be consistent in HIR too.
- parent
- .iter()
- .filter(|p| p.is_lifetime())
- .chain(child.iter().filter(|p| p.is_lifetime()))
- .chain(parent.iter().filter(|p| !p.is_lifetime()))
- .chain(child.iter().filter(|p| !p.is_lifetime()))
- .copied()
- }
+struct GenericsResolution<'a, 'tcx> {
+ trait_impl: bool,
- /// As we add hack predicates(`'a: 'a`) for all lifetimes (see `uplift_delegation_generic_params`
- /// and `generate_lifetime_predicate` functions) we need to add them to delegation generics.
- /// Those predicates will not affect resulting predicate inheritance and folding
- /// in `rustc_hir_analysis`, as we inherit all predicates from delegation signature.
- pub(super) fn all_predicates(&self) -> impl Iterator
- > {
- self.parent
- .generics
- .hir_generics_or_empty()
- .predicates
- .into_iter()
- .chain(self.child.generics.hir_generics_or_empty().predicates)
- .copied()
- }
+ parent_args: ParentSegmentArgs<'a>,
+ child_args: Option<&'a AngleBracketedArgs>,
+
+ sig_parent_params: &'tcx [ty::GenericParamDef],
+ sig_child_params: &'tcx [ty::GenericParamDef],
+
+ free_to_trait_delegation: bool,
+ /// `reuse Trait::foo;`.
+ qself_is_none: bool,
+ /// `reuse <_ as Trait>::foo;`.
+ qself_is_infer: bool,
+ /// Whether we should generate `Self` generic param.
+ generate_self: bool,
}
-impl<'hir> LoweringContext<'_, 'hir> {
- pub(super) fn uplift_delegation_generics(
- &mut self,
- delegation: &Delegation,
+impl<'hir> DelegationResolver<'_, 'hir> {
+ fn resolve_generics<'a>(
+ &self,
+ delegation: &'a Delegation,
sig_id: DefId,
- ) -> GenericsGenerationResults<'hir> {
- let delegation_parent_kind = self.tcx.def_kind(self.tcx.local_parent(self.owner.def_id));
+ ) -> GenericsResolution<'a, 'hir> {
+ let tcx = self.tcx();
+ let delegation_parent_kind = tcx.def_kind(tcx.local_parent(self.owner_id()));
- let segments = &delegation.path.segments;
- let len = segments.len();
+ let delegation_in_free_ctx =
+ !matches!(delegation_parent_kind, DefKind::Trait | DefKind::Impl { .. });
- let get_user_args = |idx: usize| -> Option<&AngleBracketedArgs> {
- let segment = &segments[idx];
+ let sig_parent = tcx.parent(sig_id);
+ let sig_in_trait = matches!(tcx.def_kind(sig_parent), DefKind::Trait);
+ let free_to_trait_delegation = delegation_in_free_ctx && sig_in_trait;
- let Some(args) = segment.args.as_ref() else { return None };
- let GenericArgs::AngleBracketed(args) = args else {
- self.tcx.dcx().span_delayed_bug(
- segment.span(),
- "expected angle-bracketed generic args in delegation segment",
- );
+ let mut sig_parent_params: &[ty::GenericParamDef] = &[];
- return None;
- };
+ let qself_is_infer =
+ delegation.qself.as_ref().is_some_and(|qself| qself.ty.is_maybe_parenthesised_infer());
- // Treat empty args `reuse foo::<> as bar` as `reuse foo as bar`,
- // the same logic applied when we call function `fn f(t: T)`
- // like that `f::<>(())`, in HIR no `<>` will be generated.
- (!args.args.is_empty()).then(|| args)
+ let qself_is_none = delegation.qself.is_none();
+
+ let parent_args = if let [.., parent_segment, _] = &delegation.path.segments[..] {
+ if let Some(res) = self.get_resolution_id(parent_segment.id)
+ && matches!(tcx.def_kind(res), DefKind::Trait | DefKind::TraitAlias)
+ {
+ sig_parent_params = &tcx.generics_of(sig_parent).own_params;
+ self.get_user_args(parent_segment)
+ .map(|args| ParentSegmentArgs::Specified(args))
+ .unwrap_or(ParentSegmentArgs::NotSpecified)
+ } else {
+ ParentSegmentArgs::Invalid
+ }
+ } else {
+ ParentSegmentArgs::Invalid
};
- let sig_params = &self.tcx.generics_of(sig_id).own_params[..];
+ GenericsResolution {
+ parent_args,
+ sig_parent_params,
+ qself_is_none,
+ qself_is_infer,
+ free_to_trait_delegation,
+ generate_self: free_to_trait_delegation && (qself_is_none || qself_is_infer),
+ trait_impl: matches!(delegation_parent_kind, DefKind::Impl { of_trait: true }),
+ sig_child_params: &tcx.generics_of(sig_id).own_params,
+ child_args: self.get_user_args(
+ delegation.path.segments.last().expect("must be at least one segment"),
+ ),
+ }
+ }
+
+ fn get_user_args<'a>(&self, segment: &'a PathSegment) -> Option<&'a AngleBracketedArgs> {
+ let Some(args) = &segment.args else { return None };
+ let GenericArgs::AngleBracketed(args) = args else {
+ self.tcx().dcx().span_delayed_bug(
+ segment.span(),
+ "expected angle-bracketed generic args in delegation segment",
+ );
+
+ return None;
+ };
+
+ // Treat empty args `reuse foo::<> as bar` as `reuse foo as bar`,
+ // the same logic applied when we call function `fn f(t: T)`
+ // like that `f::<>(())`, in HIR no `<>` will be generated.
+ (!args.args.is_empty()).then(|| args)
+ }
+
+ pub(super) fn resolve_and_generate_generics(
+ &self,
+ delegation: &Delegation,
+ sig_id: DefId,
+ ) -> GenericsGenerationResults<'hir> {
+ let res @ GenericsResolution {
+ trait_impl,
+ generate_self,
+ sig_child_params,
+ sig_parent_params,
+ ..
+ } = self.resolve_generics(delegation, sig_id);
// If we are in trait impl always generate function whose generics matches
// those that are defined in trait.
- if matches!(delegation_parent_kind, DefKind::Impl { of_trait: true }) {
+ if trait_impl {
// Considering parent generics, during signature inheritance
// we will take those args that are in trait impl header trait ref.
let parent =
@@ -312,78 +369,65 @@ impl<'hir> LoweringContext<'_, 'hir> {
let parent = GenericsGenerationResult::new(parent);
- let child = DelegationGenerics::generate_all(sig_params, GenericsPosition::Child, true);
+ let child =
+ DelegationGenerics::generate_all(sig_child_params, GenericsPosition::Child, true);
+
let child = GenericsGenerationResult::new(child);
return GenericsGenerationResults { parent, child, self_ty_propagation_kind: None };
}
- let delegation_in_free_ctx =
- !matches!(delegation_parent_kind, DefKind::Trait | DefKind::Impl { .. });
-
- let sig_parent = self.tcx.parent(sig_id);
- let sig_in_trait = matches!(self.tcx.def_kind(sig_parent), DefKind::Trait);
- let free_to_trait_delegation = delegation_in_free_ctx && sig_in_trait;
-
- let qself_is_infer =
- delegation.qself.as_ref().is_some_and(|qself| qself.ty.is_maybe_parenthesised_infer());
-
- let qself_is_none = delegation.qself.is_none();
-
- let generate_self = free_to_trait_delegation && (qself_is_none || qself_is_infer);
-
- let can_add_generics_to_parent = len >= 2
- && self.get_resolution_id(segments[len - 2].id).is_some_and(|def_id| {
- matches!(self.tcx.def_kind(def_id), DefKind::Trait | DefKind::TraitAlias)
- });
-
- let parent_generics = if can_add_generics_to_parent {
- let sig_parent_params = &self.tcx.generics_of(sig_parent).own_params;
-
- if let Some(args) = get_user_args(len - 2) {
- DelegationGenerics {
- data: self.create_slots_from_args(
- args,
- &sig_parent_params[usize::from(!generate_self)..],
- generate_self,
- ),
- pos: GenericsPosition::Parent,
- trait_impl: false,
- }
- } else {
- DelegationGenerics::generate_all(
+ let tcx = self.tcx();
+ let parent_generics = match res.parent_args {
+ ParentSegmentArgs::Specified(args) => DelegationGenerics {
+ data: Self::create_slots_from_args(
+ tcx,
+ args,
&sig_parent_params[usize::from(!generate_self)..],
- GenericsPosition::Parent,
- false,
- )
+ generate_self,
+ ),
+ pos: GenericsPosition::Parent,
+ trait_impl,
+ },
+ ParentSegmentArgs::NotSpecified => DelegationGenerics::generate_all(
+ &sig_parent_params[usize::from(!generate_self)..],
+ GenericsPosition::Parent,
+ trait_impl,
+ ),
+ ParentSegmentArgs::Invalid => {
+ DelegationGenerics { data: vec![], pos: GenericsPosition::Parent, trait_impl }
}
- } else {
- DelegationGenerics { data: vec![], pos: GenericsPosition::Parent, trait_impl: false }
};
- let child_generics = if let Some(args) = get_user_args(len - 1) {
- let synth_params_index =
- sig_params.iter().position(|p| p.kind.is_synthetic()).unwrap_or(sig_params.len());
+ let child_generics = if let Some(args) = res.child_args {
+ let synth_params_index = sig_child_params
+ .iter()
+ .position(|p| p.kind.is_synthetic())
+ .unwrap_or(sig_child_params.len());
- let mut slots =
- self.create_slots_from_args(args, &sig_params[..synth_params_index], false);
+ let mut slots = Self::create_slots_from_args(
+ tcx,
+ args,
+ &sig_child_params[..synth_params_index],
+ trait_impl,
+ );
- for synth_param in &sig_params[synth_params_index..] {
+ for synth_param in &sig_child_params[synth_params_index..] {
slots.push(GenericArgSlot::Generate(synth_param, None));
}
- DelegationGenerics { data: slots, pos: GenericsPosition::Child, trait_impl: false }
+ DelegationGenerics { data: slots, pos: GenericsPosition::Child, trait_impl }
} else {
- DelegationGenerics::generate_all(sig_params, GenericsPosition::Child, false)
+ DelegationGenerics::generate_all(sig_child_params, GenericsPosition::Child, trait_impl)
};
GenericsGenerationResults {
parent: GenericsGenerationResult::new(parent_generics),
child: GenericsGenerationResult::new(child_generics),
- self_ty_propagation_kind: match free_to_trait_delegation {
- true => Some(match qself_is_none {
+ self_ty_propagation_kind: match res.free_to_trait_delegation {
+ true => Some(match res.qself_is_none {
true => hir::DelegationSelfTyPropagationKind::SelfParam,
- false => match qself_is_infer {
+ false => match res.qself_is_infer {
true => hir::DelegationSelfTyPropagationKind::SelfParam,
// HirId is filled during generic args propagation.
false => hir::DelegationSelfTyPropagationKind::SelfTy(HirId::INVALID),
@@ -403,7 +447,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// infers than generic params then we will not process all infers thus not generating
/// more generic params then needed (anyway it is an error).
fn create_slots_from_args(
- &self,
+ tcx: TyCtxt<'_>,
args: &AngleBracketedArgs,
params: &'hir [ty::GenericParamDef],
add_first_self: bool,
@@ -444,11 +488,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
(kw::Underscore, kw::UnderscoreLifetime)
};
- self.tcx.dcx().emit_err(DelegationInfersMismatch {
- span: arg.span(),
- actual,
- expected,
- });
+ tcx.dcx().emit_err(DelegationInfersMismatch { span: arg.span(), actual, expected });
}
slots.push(match is_infer {
@@ -459,7 +499,42 @@ impl<'hir> LoweringContext<'_, 'hir> {
slots
}
+}
+
+impl<'hir> GenericsGenerationResults<'hir> {
+ pub(super) fn all_params(&self) -> impl Iterator
- > {
+ let parent = self.parent.generics.hir_generics_or_empty().params;
+ let child = self.child.generics.hir_generics_or_empty().params;
+
+ // Order generics, first we have parent and child lifetimes,
+ // then parent and child types and consts.
+ // `generics_of` in `rustc_hir_analysis` will order them anyway,
+ // however we want the order to be consistent in HIR too.
+ parent
+ .iter()
+ .filter(|p| p.is_lifetime())
+ .chain(child.iter().filter(|p| p.is_lifetime()))
+ .chain(parent.iter().filter(|p| !p.is_lifetime()))
+ .chain(child.iter().filter(|p| !p.is_lifetime()))
+ .copied()
+ }
+
+ /// As we add hack predicates(`'a: 'a`) for all lifetimes (see `uplift_delegation_generic_params`
+ /// and `generate_lifetime_predicate` functions) we need to add them to delegation generics.
+ /// Those predicates will not affect resulting predicate inheritance and folding
+ /// in `rustc_hir_analysis`, as we inherit all predicates from delegation signature.
+ pub(super) fn all_predicates(&self) -> impl Iterator
- > {
+ self.parent
+ .generics
+ .hir_generics_or_empty()
+ .predicates
+ .into_iter()
+ .chain(self.child.generics.hir_generics_or_empty().predicates)
+ .copied()
+ }
+}
+impl<'hir> LoweringContext<'_, 'hir> {
fn uplift_delegation_generic_params(
&mut self,
span: Span,
diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation/mod.rs
similarity index 58%
rename from compiler/rustc_ast_lowering/src/delegation.rs
rename to compiler/rustc_ast_lowering/src/delegation/mod.rs
index a2a7b542258d1..cd742a6c30671 100644
--- a/compiler/rustc_ast_lowering/src/delegation.rs
+++ b/compiler/rustc_ast_lowering/src/delegation/mod.rs
@@ -37,36 +37,30 @@
//! also be emitted during HIR ty lowering.
use std::iter;
-use std::ops::ControlFlow;
use ast::visit::Visitor;
-use hir::def::{DefKind, Res};
-use hir::{BodyId, HirId};
+use generics::GenericsGenerationResult;
+use hir::HirId;
+use hir::def::Res;
use rustc_abi::ExternAbi;
use rustc_ast as ast;
-use rustc_ast::node_id::NodeMap;
use rustc_ast::*;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::attrs::{AttributeKind, InlineAttr};
use rustc_hir::{self as hir, FnDeclFlags};
-use rustc_middle::span_bug;
-use rustc_middle::ty::{Asyncness, PerOwnerResolverData};
-use rustc_span::def_id::{DefId, LocalDefId};
+use rustc_middle::ty::Asyncness;
+use rustc_span::def_id::DefId;
use rustc_span::symbol::kw;
-use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym};
+use rustc_span::{Ident, Span, Symbol, sym};
-use crate::delegation::generics::{
- GenericsGenerationResult, GenericsGenerationResults, GenericsPosition,
-};
-use crate::diagnostics::{
- CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion,
- DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee,
-};
+use crate::delegation::generics::{GenericsGenerationResults, GenericsPosition};
+use crate::delegation::resolution::resolver::DelegationResolver;
+use crate::delegation::resolution::{DelegationResolution, ParamInfo};
use crate::{
AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode,
};
+mod attributes;
mod generics;
+mod resolution;
pub(crate) struct DelegationResults<'hir> {
pub body_id: hir::BodyId,
@@ -75,149 +69,24 @@ pub(crate) struct DelegationResults<'hir> {
pub generics: &'hir hir::Generics<'hir>,
}
-struct AttrAdditionInfo {
- pub equals: fn(&hir::Attribute) -> bool,
- pub kind: AttrAdditionKind,
-}
-
-enum AttrAdditionKind {
- Default { factory: fn(Span) -> hir::Attribute },
- Inherit { factory: fn(Span, &hir::Attribute) -> hir::Attribute },
-}
-
-/// Summary info about function parameters.
-#[derive(Debug, Clone, Copy, Eq, PartialEq)]
-struct ParamInfo {
- /// The number of function parameters, including any C variadic `...` parameter.
- pub param_count: usize,
-
- /// Whether the function arguments end in a C variadic `...` parameter.
- pub c_variadic: bool,
-
- /// The index of the splatted parameter, if any.
- pub splatted: Option,
-}
-
-const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO;
-
-static ATTRS_ADDITIONS: &[AttrAdditionInfo] = &[
- AttrAdditionInfo {
- equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::MustUse { .. })),
- kind: AttrAdditionKind::Inherit {
- factory: |span, original_attr| {
- let reason = match original_attr {
- hir::Attribute::Parsed(AttributeKind::MustUse { reason, .. }) => *reason,
- _ => None,
- };
-
- hir::Attribute::Parsed(AttributeKind::MustUse { span, reason })
- },
- },
- },
- AttrAdditionInfo {
- equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::Inline(..))),
- kind: AttrAdditionKind::Default {
- factory: |span| hir::Attribute::Parsed(AttributeKind::Inline(InlineAttr::Hint, span)),
- },
- },
-];
-
impl<'hir> LoweringContext<'_, 'hir> {
- fn is_method(&self, def_id: DefId, span: Span) -> bool {
- match self.tcx.def_kind(def_id) {
- DefKind::Fn => false,
- DefKind::AssocFn => self.tcx.associated_item(def_id).is_method(),
- _ => span_bug!(span, "unexpected DefKind for delegation item"),
- }
- }
-
- fn check_for_cycles(&self, mut def_id: DefId, span: Span) -> Result<(), ErrorGuaranteed> {
- let mut visited: FxHashSet = Default::default();
-
- loop {
- visited.insert(def_id);
-
- // If def_id is in local crate and it corresponds to another delegation
- // it means that we refer to another delegation as a callee, so in order to obtain
- // a signature DefId we obtain NodeId of the callee delegation and try to get signature from it.
- if let Some(local_id) = def_id.as_local()
- && let Some(info) = self.tcx.resolutions(()).delegation_infos.get(&local_id)
- && let Ok(id) = info.resolution_id
- {
- def_id = id;
- if visited.contains(&def_id) {
- return Err(match visited.len() {
- 1 => self.dcx().emit_err(UnresolvedDelegationCallee { span }),
- _ => self.dcx().emit_err(CycleInDelegationSignatureResolution { span }),
- });
- }
- } else {
- return Ok(());
- }
- }
- }
-
- pub(crate) fn lower_delegation(
- &mut self,
- delegation: &Delegation,
- item_id: NodeId,
- ) -> DelegationResults<'hir> {
+ pub(crate) fn lower_delegation(&mut self, delegation: &Delegation) -> DelegationResults<'hir> {
let span = self.lower_span(delegation.last_segment_span());
- let Some(info) = self.tcx.resolutions(()).delegation_infos.get(&self.owner.def_id) else {
- self.dcx().span_delayed_bug(
- span,
- format!("delegation resolution record was not found for {:?}", self.owner.def_id),
- );
-
- return self.generate_delegation_error(span, delegation);
- };
-
- let sig_id = info.resolution_id.and_then(|id| self.check_for_cycles(id, span).map(|_| id));
-
- // Delegation can be missing from the `delegations_resolutions` table
- // in illegal places such as function bodies in extern blocks (see #151356).
- let Ok(sig_id) = sig_id else {
- self.dcx().span_delayed_bug(
- span,
- format!("LoweringContext: the delegation {:?} is unresolved", item_id),
- );
-
+ let resolver = DelegationResolver::new(self);
+ let Ok((res, mut generics)) = resolver.resolve_delegation(delegation, span) else {
return self.generate_delegation_error(span, delegation);
};
- self.add_attrs_if_needed(span, sig_id);
+ self.add_attrs_if_needed(&res);
- let is_method = self.is_method(sig_id, span);
+ let (body_id, call_expr_id, unused_target_expr) =
+ self.lower_delegation_body(delegation, &res, &mut generics);
- let param_info = self.param_info(sig_id);
+ let decl = self.lower_delegation_decl(&res, &generics, call_expr_id, unused_target_expr);
- if !self.check_block_soundness(delegation, sig_id, is_method, param_info.param_count) {
- return self.generate_delegation_error(span, delegation);
- }
+ let sig = self.lower_delegation_sig(res.sig_id, decl, span);
- let mut generics = self.uplift_delegation_generics(delegation, sig_id);
-
- let (body_id, call_expr_id, unused_target_expr) = self.lower_delegation_body(
- delegation,
- sig_id,
- param_info.param_count,
- &mut generics,
- span,
- );
-
- let decl = self.lower_delegation_decl(
- delegation.source,
- sig_id,
- param_info,
- span,
- &generics,
- delegation.id,
- call_expr_id,
- unused_target_expr,
- );
-
- let sig = self.lower_delegation_sig(sig_id, decl, span);
let ident = self.lower_ident(delegation.ident);
let generics = self.arena.alloc(hir::Generics {
@@ -231,160 +100,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
DelegationResults { body_id, sig, ident, generics }
}
- fn check_block_soundness(
- &self,
- delegation: &Delegation,
- sig_id: DefId,
- is_method: bool,
- param_count: usize,
- ) -> bool {
- let Some(block) = delegation.body.as_ref() else { return true };
- let should_generate_block = self.should_generate_block(delegation, sig_id, is_method);
-
- // Report an error if user has explicitly specified delegation's target expression
- // in a single delegation when reused function has no params.
- if param_count == 0 && should_generate_block {
- self.dcx().emit_err(DelegationBlockSpecifiedWhenNoParams { span: block.span });
- return false;
- }
-
- struct DefinitionsFinder<'a> {
- all_owners: &'a NodeMap>,
- // `self.owner.node_id_to_def_id`
- nested_def_ids: &'a NodeMap,
- }
-
- impl<'a> ast::visit::Visitor<'a> for DefinitionsFinder<'a> {
- type Result = ControlFlow<()>;
-
- fn visit_id(&mut self, id: NodeId) -> Self::Result {
- /*
- (from `tests\ui\delegation\target-expr-removal-defs-inside.rs`):
- ```rust
- reuse impl Trait for S1 {
- some::path::<{ fn foo() {} }>::xd();
- fn foo() {}
- self.0
- }
- ```
-
- Constant from unresolved path will be in `nested_owners`,
- `fn foo() {}` will not be in `nested_owners` but will be in `owners`,
- both have `LocalDefId`, so we check those two maps.
- */
- match self.all_owners.contains_key(&id) || self.nested_def_ids.contains_key(&id) {
- true => ControlFlow::Break(()),
- false => ControlFlow::Continue(()),
- }
- }
- }
-
- let mut collector = DefinitionsFinder {
- all_owners: &self.resolver.owners,
- nested_def_ids: &self.owner.node_id_to_def_id,
- };
-
- let contains_defs = collector.visit_block(block).is_break();
-
- // If there are definitions inside and we can't delete target expression, so report an error.
- // FIXME(fn_delegation): support deletion of target expression with defs inside.
- if !should_generate_block && contains_defs {
- self.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span });
- return false;
- }
-
- true
- }
-
- fn should_generate_block(
- &self,
- delegation: &Delegation,
- sig_id: DefId,
- is_method: bool,
- ) -> bool {
- is_method
- || matches!(self.tcx.def_kind(sig_id), DefKind::Fn)
- || matches!(delegation.source, DelegationSource::Single)
- }
-
- fn add_attrs_if_needed(&mut self, span: Span, sig_id: DefId) {
- let new_attrs =
- self.create_new_attrs(ATTRS_ADDITIONS, span, sig_id, self.attrs.get(&PARENT_ID));
-
- if new_attrs.is_empty() {
- return;
- }
-
- let new_arena_allocated_attrs = match self.attrs.get(&PARENT_ID) {
- Some(existing_attrs) => self.arena.alloc_from_iter(
- existing_attrs.iter().map(|a| a.clone()).chain(new_attrs.into_iter()),
- ),
- None => self.arena.alloc_from_iter(new_attrs.into_iter()),
- };
-
- self.attrs.insert(PARENT_ID, new_arena_allocated_attrs);
- }
-
- fn create_new_attrs(
- &self,
- candidate_additions: &[AttrAdditionInfo],
- span: Span,
- sig_id: DefId,
- existing_attrs: Option<&&[hir::Attribute]>,
- ) -> Vec {
- candidate_additions
- .iter()
- .filter_map(|addition_info| {
- if let Some(existing_attrs) = existing_attrs
- && existing_attrs
- .iter()
- .any(|existing_attr| (addition_info.equals)(existing_attr))
- {
- return None;
- }
-
- match addition_info.kind {
- AttrAdditionKind::Default { factory } => Some(factory(span)),
- AttrAdditionKind::Inherit { factory, .. } =>
- {
- #[allow(deprecated)]
- self.tcx
- .get_all_attrs(sig_id)
- .iter()
- .find_map(|a| (addition_info.equals)(a).then(|| factory(span, a)))
- }
- }
- })
- .collect::>()
- }
-
- fn get_resolution_id(&self, node_id: NodeId) -> Option {
- self.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id())
- }
-
- /// Returns function parameter info, including C variadic `...` and `#[splat]` if present.
- fn param_info(&self, def_id: DefId) -> ParamInfo {
- let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder();
-
- ParamInfo {
- param_count: sig.inputs().len() + usize::from(sig.c_variadic()),
- c_variadic: sig.c_variadic(),
- splatted: sig.splatted(),
- }
- }
-
fn lower_delegation_decl(
&mut self,
- source: DelegationSource,
- sig_id: DefId,
- param_info: ParamInfo,
- span: Span,
+ res: &DelegationResolution,
generics: &GenericsGenerationResults<'hir>,
- call_path_node_id: NodeId,
call_expr_id: HirId,
unused_target_expr: bool,
) -> &'hir hir::FnDecl<'hir> {
- let ParamInfo { param_count, c_variadic, splatted } = param_info;
+ let &DelegationResolution { source, call_path_res, span, sig_id, .. } = res;
+ let ParamInfo { param_count, c_variadic, splatted } = res.param_info;
// The last parameter in C variadic functions is skipped in the signature,
// like during regular lowering.
@@ -404,7 +128,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig_id,
hir::InferDelegationSig::Output(self.arena.alloc(hir::DelegationInfo {
call_expr_id,
- call_path_res: self.get_resolution_id(call_path_node_id),
+ call_path_res,
child_seg_id: generics.child.args_segment_id,
child_seg_id_for_sig: generics.child.segment_id_for_sig(),
parent_seg_id_for_sig: generics.parent.segment_id_for_sig(),
@@ -517,23 +241,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_delegation_body(
&mut self,
delegation: &Delegation,
- sig_id: DefId,
- param_count: usize,
+ res: &DelegationResolution,
generics: &mut GenericsGenerationResults<'hir>,
- span: Span,
- ) -> (BodyId, HirId, bool) {
+ ) -> (hir::BodyId, HirId, bool) {
let block = delegation.body.as_deref();
let mut call_expr_id = HirId::INVALID;
let mut unused_target_expr = false;
let block_id = self.lower_body(|this| {
+ let &DelegationResolution {
+ param_info, span, should_generate_block, is_method, ..
+ } = res;
+
+ let ParamInfo { param_count, .. } = param_info;
+
let mut parameters: Vec> = Vec::with_capacity(param_count);
let mut args: Vec> = Vec::with_capacity(param_count);
let mut stmts: &[hir::Stmt<'hir>] = &[];
- let is_method = this.is_method(sig_id, span);
- let should_generate_block = this.should_generate_block(delegation, sig_id, is_method);
-
// Consider non-specified target expression as generated,
// as we do not want to emit error when target expression is
// not specified.
@@ -580,7 +305,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
let (final_expr, hir_id) =
- this.finalize_body_lowering(delegation, stmts, args, generics, span);
+ this.finalize_body_lowering(delegation, stmts, args, res, generics, span);
call_expr_id = hir_id;
@@ -597,6 +322,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
delegation: &Delegation,
stmts: &'hir [hir::Stmt<'hir>],
args: Vec>,
+ res: &DelegationResolution,
generics: &mut GenericsGenerationResults<'hir>,
span: Span,
) -> (hir::Expr<'hir>, HirId) {
@@ -666,7 +392,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let args = self.arena.alloc_from_iter(args);
let call = self.mk_expr(hir::ExprKind::Call(callee_path, args), span);
- let expr = if let Some((parent, of_trait)) = self.should_wrap_return_value(delegation) {
+ let expr = if let Some((parent, of_trait)) = res.output_self_mapping {
let res = Res::SelfTyAlias { alias_to: parent.to_def_id(), is_trait_impl: of_trait };
let ident = Ident::new(kw::SelfUpper, span);
let path = self.create_resolved_path(res, ident, span);
@@ -701,45 +427,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
(self.mk_expr(hir::ExprKind::Block(block, None), span), call.hir_id)
}
- fn should_wrap_return_value(&self, delegation: &Delegation) -> Option<(LocalDefId, bool)> {
- // Heuristic: don't do wrapping if there is no target expression.
- if delegation.body.is_none() {
- return None;
- }
-
- let tcx = self.tcx;
- let parent = tcx.local_parent(self.owner.def_id);
- let parent_kind = tcx.def_kind(parent);
-
- // Apply wrapping for delegations inside
- // 1) Trait impls, as the return type of both signature function
- // and generated delegation has `Self` generic param returned
- // (checked below).
- // FIXME(fn_delegation): think of enabling wrapping in more scenarios:
- // trait-(impl)-to-free
- // trait-(impl)-to-inherent
- // inherent-to-free
- // 2) Inherent methods when delegating to trait, as we change the type of
- // `Self` to type of struct or enum we delegate from.
- if !matches!(tcx.def_kind(parent), DefKind::Impl { .. }) {
- return None;
- }
-
- let is_trait_impl = parent_kind == DefKind::Impl { of_trait: true };
-
- // Check that delegation path resolves to a trait AssocFn, not to a free method.
- Some((parent, is_trait_impl)).filter(|_| {
- self.get_resolution_id(delegation.id).is_some_and(|id| {
- tcx.def_kind(id) == DefKind::AssocFn
- // Check that the return type of the callee is `Self` param.
- // After previous check we are sure that `sig_id` and `delegation.id`
- // point to the same function.
- && tcx.def_kind(tcx.parent(id)) == DefKind::Trait
- && tcx.fn_sig(id).skip_binder().output().skip_binder().is_param(0)
- })
- })
- }
-
fn process_segment(
&mut self,
span: Span,
@@ -808,7 +495,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
let callee_path = this.arena.alloc(this.mk_expr(hir::ExprKind::Path(path), span));
- let args = if let Some(block) = delegation.body.as_ref() {
+ let args = if let Some(block) = &delegation.body {
this.arena.alloc_slice(&[this.lower_block_expr(block)])
} else {
&mut []
diff --git a/compiler/rustc_ast_lowering/src/delegation/resolution.rs b/compiler/rustc_ast_lowering/src/delegation/resolution.rs
new file mode 100644
index 0000000000000..3cb40d256ba8b
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/delegation/resolution.rs
@@ -0,0 +1,264 @@
+use std::ops::ControlFlow;
+
+use ast::visit::Visitor;
+use hir::def::DefKind;
+use rustc_ast as ast;
+use rustc_ast::*;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir as hir;
+use rustc_middle::span_bug;
+use rustc_span::def_id::{DefId, LocalDefId};
+use rustc_span::{ErrorGuaranteed, Span};
+
+use crate::delegation::generics::GenericsGenerationResults;
+use crate::delegation::resolution::resolver::DelegationResolver;
+use crate::diagnostics::{
+ CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion,
+ DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee,
+};
+
+/// Summary info about function parameters.
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub(super) struct ParamInfo {
+ /// The number of function parameters, including any C variadic `...` parameter.
+ pub param_count: usize,
+
+ /// Whether the function arguments end in a C variadic `...` parameter.
+ pub c_variadic: bool,
+
+ /// The index of the splatted parameter, if any.
+ pub splatted: Option,
+}
+
+pub(super) struct DelegationResolution {
+ pub sig_id: DefId,
+ pub is_method: bool,
+ pub param_info: ParamInfo,
+ pub span: Span,
+ pub should_generate_block: bool,
+ pub call_path_res: Option,
+ pub source: DelegationSource,
+ pub output_self_mapping: Option<(LocalDefId, bool)>,
+}
+
+pub(super) mod resolver {
+ use rustc_ast::NodeId;
+ use rustc_hir::def_id::{DefId, LocalDefId};
+ use rustc_middle::ty::TyCtxt;
+
+ use crate::LoweringContext;
+
+ /// Abstracts operations that are needed for delegation's resolution, so resolution
+ /// is independent of `LoweringContext`. Placed in a separate module so `LoweringContext`
+ /// can not be accessed directly.
+ pub(crate) struct DelegationResolver<'a, 'hir>(&'a LoweringContext<'a, 'hir>);
+
+ impl<'a, 'tcx> DelegationResolver<'a, 'tcx> {
+ pub(crate) fn new(ctx: &'a LoweringContext<'a, 'tcx>) -> Self {
+ DelegationResolver(ctx)
+ }
+
+ #[inline]
+ pub(crate) fn tcx(&self) -> TyCtxt<'tcx> {
+ self.0.tcx
+ }
+
+ #[inline]
+ pub(crate) fn owner_id(&self) -> LocalDefId {
+ self.0.owner.def_id
+ }
+
+ /// (from `tests\ui\delegation\target-expr-removal-defs-inside.rs`):
+ /// ```rust
+ /// reuse impl Trait for S1 {
+ /// some::path::<{ fn foo() {} }>::xd();
+ /// fn foo() {}
+ /// self.0
+ /// }
+ /// ```
+ ///
+ /// Constant from unresolved path will be in `node_id_to_def_id`,
+ /// `fn foo() {}` will not be in `node_id_to_def_id` but will be in `owners`,
+ /// both have `LocalDefId`, so we check those two maps.
+ #[inline]
+ pub(crate) fn is_definition(&self, id: NodeId) -> bool {
+ self.0.resolver.owners.contains_key(&id)
+ || self.0.owner.node_id_to_def_id.contains_key(&id)
+ }
+
+ #[inline]
+ pub(crate) fn get_resolution_id(&self, id: NodeId) -> Option {
+ self.0.get_partial_res(id).and_then(|r| r.expect_full_res().opt_def_id())
+ }
+ }
+}
+
+impl<'tcx> DelegationResolver<'_, 'tcx> {
+ pub(super) fn resolve_delegation(
+ &self,
+ delegation: &Delegation,
+ span: Span,
+ ) -> Result<(DelegationResolution, GenericsGenerationResults<'tcx>), ErrorGuaranteed> {
+ let tcx = self.tcx();
+ let def_id = self.owner_id();
+
+ // Delegation can be missing from the `delegations_resolutions` table
+ // in illegal places such as function bodies in extern blocks (see #151356).
+ let sig_id = tcx
+ .resolutions(())
+ .delegation_infos
+ .get(&def_id)
+ .map(|info| {
+ info.resolution_id.and_then(|id| self.check_for_cycles(id, span).map(|_| id))
+ })
+ .unwrap_or_else(|| {
+ Err(tcx.dcx().span_delayed_bug(
+ span,
+ format!("delegation resolution record was not found for {:?}", def_id),
+ ))
+ })?;
+
+ let is_method = match tcx.def_kind(sig_id) {
+ DefKind::Fn => false,
+ DefKind::AssocFn => tcx.associated_item(sig_id).is_method(),
+ _ => span_bug!(span, "unexpected DefKind for delegation item"),
+ };
+
+ let sig = tcx.fn_sig(sig_id).skip_binder().skip_binder();
+ let param_count = sig.inputs().len() + usize::from(sig.c_variadic());
+
+ let res = DelegationResolution {
+ is_method,
+ span,
+ sig_id,
+ // FIXME(splat): use `sig.splatted()` once FnSig has it
+ param_info: ParamInfo { param_count, c_variadic: sig.c_variadic(), splatted: None },
+ should_generate_block: self.check_block_soundness(
+ delegation,
+ sig_id,
+ is_method,
+ param_count,
+ )?,
+ source: delegation.source,
+ call_path_res: self.get_resolution_id(delegation.id),
+ output_self_mapping: self.should_map_return_value(delegation),
+ };
+
+ Ok((res, self.resolve_and_generate_generics(delegation, sig_id)))
+ }
+
+ fn check_for_cycles(&self, mut def_id: DefId, span: Span) -> Result<(), ErrorGuaranteed> {
+ let tcx = self.tcx();
+ let mut visited: FxHashSet = Default::default();
+
+ loop {
+ visited.insert(def_id);
+
+ // If def_id is in local crate and it corresponds to another delegation
+ // it means that we refer to another delegation as a callee, so in order to obtain
+ // a signature DefId we obtain NodeId of the callee delegation and try to get signature from it.
+ if let Some(local_id) = def_id.as_local()
+ && let Some(info) = tcx.resolutions(()).delegation_infos.get(&local_id)
+ && let Ok(id) = info.resolution_id
+ {
+ def_id = id;
+ if visited.contains(&def_id) {
+ return Err(match visited.len() {
+ 1 => tcx.dcx().emit_err(UnresolvedDelegationCallee { span }),
+ _ => tcx.dcx().emit_err(CycleInDelegationSignatureResolution { span }),
+ });
+ }
+ } else {
+ return Ok(());
+ }
+ }
+ }
+
+ fn check_block_soundness(
+ &self,
+ delegation: &Delegation,
+ sig_id: DefId,
+ is_method: bool,
+ param_count: usize,
+ ) -> Result {
+ let tcx = self.tcx();
+ let should_generate_block = is_method
+ || matches!(tcx.def_kind(sig_id), DefKind::Fn)
+ || matches!(delegation.source, DelegationSource::Single);
+
+ let Some(block) = &delegation.body else { return Ok(should_generate_block) };
+
+ // Report an error if user has explicitly specified delegation's target expression
+ // in a single delegation when reused function has no params.
+ if param_count == 0 && should_generate_block {
+ let err = DelegationBlockSpecifiedWhenNoParams { span: block.span };
+ return Err(tcx.dcx().emit_err(err));
+ }
+
+ struct DefinitionsFinder<'a, 'hir> {
+ ctx: &'a DelegationResolver<'a, 'hir>,
+ }
+
+ impl<'a> Visitor<'a> for DefinitionsFinder<'a, '_> {
+ type Result = ControlFlow<()>;
+
+ fn visit_id(&mut self, id: NodeId) -> Self::Result {
+ match self.ctx.is_definition(id) {
+ true => ControlFlow::Break(()),
+ false => ControlFlow::Continue(()),
+ }
+ }
+ }
+
+ let mut collector = DefinitionsFinder { ctx: self };
+
+ let contains_defs = collector.visit_block(block).is_break();
+
+ // If there are definitions inside and we can't delete target expression, then report an error.
+ // FIXME(fn_delegation): support deletion of target expression with defs inside.
+ if should_generate_block || !contains_defs {
+ Ok(should_generate_block)
+ } else {
+ Err(tcx.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span }))
+ }
+ }
+
+ fn should_map_return_value(&self, delegation: &Delegation) -> Option<(LocalDefId, bool)> {
+ // Heuristic: don't do wrapping if there is no target expression.
+ if delegation.body.is_none() {
+ return None;
+ }
+
+ let tcx = self.tcx();
+ let parent = tcx.local_parent(self.owner_id());
+ let parent_kind = tcx.def_kind(parent);
+
+ // Apply wrapping for delegations inside
+ // 1) Trait impls, as the return type of both signature function
+ // and generated delegation has `Self` generic param returned
+ // (checked below).
+ // FIXME(fn_delegation): think of enabling wrapping in more scenarios:
+ // trait-(impl)-to-free
+ // trait-(impl)-to-inherent
+ // inherent-to-free
+ // 2) Inherent methods when delegating to trait, as we change the type of
+ // `Self` to type of struct or enum we delegate from.
+ if !matches!(tcx.def_kind(parent), DefKind::Impl { .. }) {
+ return None;
+ }
+
+ let is_trait_impl = parent_kind == DefKind::Impl { of_trait: true };
+
+ // Check that delegation path resolves to a trait AssocFn, not to a free method.
+ Some((parent, is_trait_impl)).filter(|_| {
+ self.get_resolution_id(delegation.id).is_some_and(|id| {
+ tcx.def_kind(id) == DefKind::AssocFn
+ // Check that the return type of the callee is `Self` param.
+ // After previous check we are sure that `sig_id` and `delegation.id`
+ // point to the same function.
+ && tcx.def_kind(tcx.parent(id)) == DefKind::Trait
+ && tcx.fn_sig(id).skip_binder().output().skip_binder().is_param(0)
+ })
+ })
+ }
+}
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index c54777d98070a..8ebecb222b940 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -567,7 +567,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ItemKind::Macro(ident, macro_def, macro_kinds)
}
ItemKind::Delegation(delegation) => {
- let delegation_results = self.lower_delegation(delegation, id);
+ let delegation_results = self.lower_delegation(delegation);
hir::ItemKind::Fn {
sig: delegation_results.sig,
ident: delegation_results.ident,
@@ -1052,7 +1052,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
(*ident, generics, kind, ty.is_some())
}
AssocItemKind::Delegation(delegation) => {
- let delegation_results = self.lower_delegation(delegation, i.id);
+ let delegation_results = self.lower_delegation(delegation);
let item_kind = hir::TraitItemKind::Fn(
delegation_results.sig,
hir::TraitFn::Provided(delegation_results.body_id),
@@ -1264,7 +1264,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
}
AssocItemKind::Delegation(delegation) => {
- let delegation_results = self.lower_delegation(delegation, i.id);
+ let delegation_results = self.lower_delegation(delegation);
(
delegation.ident,
(
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index f6abafb874165..1e336f1e4509d 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -585,11 +585,11 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
NllRegionVariableOrigin::FreeRegion,
outlived_region,
);
- let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
+ let BlameConstraint { category, from_closure, span, .. } = blame_constraint;
let outlived_fr_name = self.give_region_a_name(outlived_region);
- (category, from_closure, cause.span, outlived_fr_name, path)
+ (category, from_closure, span, outlived_fr_name, path)
}
/// Returns structured explanation for *why* the borrow contains the
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 1166e1265dadb..a82cd5c74c330 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -414,8 +414,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
};
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
- let cause =
- self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid).0.cause;
+ let (blame_constraint, path) =
+ self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid);
+ let cause = blame_constraint.to_obligation_cause_from_path(&path);
// FIXME these methods should have better names, and also probably not be this generic.
// FIXME note that we *throw away* the error element here! We probably want to
@@ -448,17 +449,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let (blame_constraint, path) =
self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr);
- let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
+ let BlameConstraint { category, span, variance_info, .. } = blame_constraint;
- debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
+ debug!("report_region_error: category={:?} {:?} {:?}", category, span, variance_info);
// Check if we can use one of the "nice region errors".
if let (Some(f), Some(o)) =
(self.regioncx.to_error_region(fr), self.regioncx.to_error_region(outlived_fr))
{
let infer_err = self.infcx.err_ctxt();
- let nice =
- NiceRegionError::new_from_span(&infer_err, self.mir_def_id(), cause.span, o, f);
+ let nice = NiceRegionError::new_from_span(&infer_err, self.mir_def_id(), span, o, f);
if let Some(diag) = nice.try_report_from_nll() {
self.buffer_error(diag);
return;
@@ -475,7 +475,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
fr_is_local, outlived_fr_is_local, category
);
- let errci = ErrorConstraintInfo { fr, outlived_fr, category, span: cause.span };
+ let errci = ErrorConstraintInfo { fr, outlived_fr, category, span };
let mut diag = match (category, fr_is_local, outlived_fr_is_local) {
(ConstraintCategory::SolverRegionConstraint(span), _, _) => {
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index 5e56ae80ff5da..64aa92c2c8878 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -1346,7 +1346,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
subject: ClosureOutlivesSubject::Region(fr_minus),
outlived_free_region: fr_plus,
- blame_span: blame_constraint.cause.span,
+ blame_span: blame_constraint.span,
category: blame_constraint.category,
});
}
@@ -1652,24 +1652,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.collect::>()
);
- // We try to avoid reporting a `ConstraintCategory::Predicate` as our best constraint.
- // Instead, we use it to produce an improved `ObligationCauseCode`.
- // FIXME - determine what we should do if we encounter multiple
- // `ConstraintCategory::Predicate` constraints. Currently, we just pick the first one.
- let cause_code = path
- .iter()
- .find_map(|constraint| {
- if let ConstraintCategory::Predicate(predicate_span) = constraint.category {
- // We currently do not store the `DefId` in the `ConstraintCategory`
- // for performances reasons. The error reporting code used by NLL only
- // uses the span, so this doesn't cause any problems at the moment.
- Some(ObligationCauseCode::WhereClause(CRATE_DEF_ID.to_def_id(), predicate_span))
- } else {
- None
- }
- })
- .unwrap_or_else(|| ObligationCauseCode::Misc);
-
// When reporting an error, there is typically a chain of constraints leading from some
// "source" region which must outlive some "target" region.
// In most cases, we prefer to "blame" the constraints closer to the target --
@@ -1836,7 +1818,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let blame_constraint = BlameConstraint {
category: best_constraint.category,
from_closure: best_constraint.from_closure,
- cause: ObligationCause::new(best_constraint.span, CRATE_DEF_ID, cause_code.clone()),
+ span: best_constraint.span,
variance_info: best_constraint.variance_info,
};
(blame_constraint, path)
@@ -1917,6 +1899,31 @@ impl<'tcx> RegionInferenceContext<'tcx> {
pub(crate) struct BlameConstraint<'tcx> {
pub category: ConstraintCategory<'tcx>,
pub from_closure: bool,
- pub cause: ObligationCause<'tcx>,
+ pub span: Span,
pub variance_info: ty::VarianceDiagInfo>,
}
+
+impl<'tcx> BlameConstraint<'tcx> {
+ pub(crate) fn to_obligation_cause_from_path(
+ &self,
+ path: &[OutlivesConstraint<'tcx>],
+ ) -> ObligationCause<'tcx> {
+ // FIXME - determine what we should do if we encounter multiple
+ // `ConstraintCategory::Predicate` constraints. Currently, we just pick the first one.
+ let cause_code = path
+ .iter()
+ .find_map(|constraint| {
+ if let ConstraintCategory::Predicate(predicate_span) = constraint.category {
+ // We currently do not store the `DefId` in the `ConstraintCategory`
+ // for performances reasons. The error reporting code used by NLL only
+ // uses the span, so this doesn't cause any problems at the moment.
+ Some(ObligationCauseCode::WhereClause(CRATE_DEF_ID.to_def_id(), predicate_span))
+ } else {
+ None
+ }
+ })
+ .unwrap_or_else(|| ObligationCauseCode::Misc);
+
+ ObligationCause::new(self.span, CRATE_DEF_ID, cause_code.clone())
+ }
+}
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index 020c6668fb9d3..b2d22876c1858 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -620,7 +620,9 @@ pub(crate) fn run_pass_manager(
if cfg!(feature = "llvm_enzyme") && enable_ad && !thin {
let opt_stage = llvm::OptStage::FatLTO;
let stage = write::AutodiffStage::PostAD;
- if !config.autodiff.contains(&config::AutoDiff::NoPostopt) {
+ if !config.autodiff.contains(&config::AutoDiff::NoPostopt)
+ && config.autodiff_post_passes.as_deref() != Some("")
+ {
unsafe {
write::llvm_optimize(
cgcx, prof, dcx, module, None, None, config, opt_level, opt_stage, stage,
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 4426f6ebb3c17..e44c579e06c83 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -566,6 +566,14 @@ pub(crate) unsafe fn llvm_optimize(
let print_before_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModBefore);
let print_after_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModAfter);
let print_passes = config.autodiff.contains(&config::AutoDiff::PrintPasses);
+ let passes_after_enzyme = if autodiff_stage == AutodiffStage::PostAD {
+ config.autodiff_post_passes.as_deref()
+ } else {
+ None
+ };
+ let passes_after_enzyme_ptr =
+ passes_after_enzyme.map_or(std::ptr::null(), |s| s.as_c_char_ptr());
+ let passes_after_enzyme_len = passes_after_enzyme.map_or(0, |s| s.len());
let merge_functions;
let unroll_loops;
let vectorize_slp;
@@ -795,6 +803,8 @@ pub(crate) unsafe fn llvm_optimize(
llvm_selfprofiler,
selfprofile_before_pass_callback,
selfprofile_after_pass_callback,
+ passes_after_enzyme_ptr,
+ passes_after_enzyme_len,
extra_passes.as_c_char_ptr(),
extra_passes.len(),
llvm_plugins.as_c_char_ptr(),
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index bd770d286851b..56c90582e4642 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -2492,6 +2492,8 @@ unsafe extern "C" {
llvm_selfprofiler: *mut c_void,
begin_callback: SelfProfileBeforePassCallback,
end_callback: SelfProfileAfterPassCallback,
+ PostEnzymePasses: *const c_char,
+ PostEnzymePassesLen: size_t,
ExtraPasses: *const c_char,
ExtraPassesLen: size_t,
LLVMPlugins: *const c_char,
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index de1adf02a049d..1419fcaf733f3 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -106,6 +106,7 @@ pub struct ModuleConfig {
pub emit_lifetime_markers: bool,
pub llvm_plugins: Vec,
pub autodiff: Vec,
+ pub autodiff_post_passes: Option,
pub offload: Vec,
}
@@ -257,6 +258,10 @@ impl ModuleConfig {
emit_lifetime_markers: sess.emit_lifetime_markers(),
llvm_plugins: if_regular!(sess.opts.unstable_opts.llvm_plugins.clone(), vec![]),
autodiff: if_regular!(sess.opts.unstable_opts.autodiff.clone(), vec![]),
+ autodiff_post_passes: if_regular!(
+ sess.opts.unstable_opts.autodiff_post_passes.clone(),
+ None
+ ),
offload: if_regular!(sess.opts.unstable_opts.offload.clone(), vec![]),
}
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index 99546bea65959..413cb5f2c860a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -63,6 +63,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
result_place: Option>,
source_info: SourceInfo,
) -> IntrinsicResult<'tcx, Bx::Value> {
+ // When `-Zforce-intrinsic-fallback` is enabled, always use the fallback body if it exists,
+ if bx.tcx().sess.opts.unstable_opts.force_intrinsic_fallback
+ && let Some(def) = bx.tcx().intrinsic(instance.def_id())
+ && !def.must_be_overridden
+ {
+ return IntrinsicResult::Fallback(ty::Instance::new_raw(
+ instance.def_id(),
+ instance.args,
+ ));
+ }
+
let span = source_info.span;
let name = bx.tcx().item_name(instance.def_id());
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 1aeaaee3a251c..0926476c9e01b 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -552,16 +552,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let old_stack = self.frame_idx();
let old_loc = self.frame().loc;
+ // Evaluation order consistent with assignment: destination first.
+ let dest_place = self.eval_place(destination)?;
let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } =
self.eval_callee_and_args(terminator, func, args, &destination)?;
- let destination = self.eval_place(destination)?;
self.init_fn_call(
callee,
(fn_sig.abi(), fn_abi),
&args,
with_caller_location,
- &destination,
+ &dest_place,
target,
if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable },
)?;
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 0fd21e017ff61..c6bbeda36e930 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -5,7 +5,7 @@ use std::{iter, mem, slice};
use rustc_ast::mut_visit::*;
use rustc_ast::tokenstream::TokenStream;
-use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list};
+use rustc_ast::visit::{AssocCtxt, Visitor, VisitorResult, try_visit, walk_list};
use rustc_ast::{
self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrItemKind, AttrStyle, AttrVec,
DUMMY_NODE_ID, DelegationSource, DelegationSuffixes, EarlyParsedAttribute, ExprKind,
@@ -20,7 +20,7 @@ use rustc_attr_parsing::{
};
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::{PResult, msg};
+use rustc_errors::PResult;
use rustc_feature::Features;
use rustc_hir::Target;
use rustc_hir::def::MacroKinds;
@@ -29,7 +29,6 @@ use rustc_parse::parser::{
AllowConstBlockItems, AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser,
RecoverColon, RecoverComma, Recovery, token_descr,
};
-use rustc_session::Session;
use rustc_session::errors::feature_err;
use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
use rustc_span::hygiene::SyntaxContext;
@@ -770,7 +769,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
InvocationKind::Attr { attr, pos, mut item, derives } => {
if let Some(expander) = ext.as_attr() {
- self.gate_proc_macro_input(&item);
self.gate_proc_macro_attr_item(span, &item);
let tokens = match &item {
// FIXME: Collect tokens and use them instead of generating
@@ -904,9 +902,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
InvocationKind::Derive { path, item, is_const } => match ext {
SyntaxExtensionKind::Derive(expander)
| SyntaxExtensionKind::LegacyDerive(expander) => {
- if let SyntaxExtensionKind::Derive(..) = ext {
- self.gate_proc_macro_input(&item);
- }
// The `MetaItem` representing the trait to derive can't
// have an unsafe around it (as of now).
let meta = ast::MetaItem {
@@ -1043,37 +1038,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
.emit();
}
- fn gate_proc_macro_input(&self, annotatable: &Annotatable) {
- struct GateProcMacroInput<'a> {
- sess: &'a Session,
- }
-
- impl<'ast, 'a> Visitor<'ast> for GateProcMacroInput<'a> {
- fn visit_item(&mut self, item: &'ast ast::Item) {
- match &item.kind {
- ItemKind::Mod(_, _, mod_kind)
- if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) =>
- {
- feature_err(
- self.sess,
- sym::proc_macro_hygiene,
- item.span,
- msg!("file modules in proc macro input are unstable"),
- )
- .emit();
- }
- _ => {}
- }
-
- visit::walk_item(self, item);
- }
- }
-
- if !self.cx.ecfg.features.proc_macro_hygiene() {
- annotatable.visit_with(&mut GateProcMacroInput { sess: self.cx.sess });
- }
- }
-
fn parse_ast_fragment(
&mut self,
toks: TokenStream,
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index fc38d9de6e143..12fb5c9ced7f3 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -696,7 +696,7 @@ declare_features! (
(unstable, powerpc_target_feature, "1.27.0", Some(150255)),
/// The prfchw target feature on x86.
(unstable, prfchw_target_feature, "1.78.0", Some(150256)),
- /// Allows macro attributes on expressions, statements and non-inline modules.
+ /// Allows macro attributes on expressions and statements.
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
/// Allows the use of raw-dylibs on ELF platforms
(incomplete, raw_dylib_elf, "1.87.0", Some(135694)),
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 1b174631aa890..4ff870b405e5a 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -10,7 +10,7 @@ use rustc_hir::ItemKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::{self, InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt};
-use rustc_infer::traits::{Obligation, PredicateObligations};
+use rustc_infer::traits::Obligation;
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::relate::solver_relating::RelateExt;
@@ -469,37 +469,6 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
}
}
-fn structurally_normalize_ty<'tcx>(
- tcx: TyCtxt<'tcx>,
- infcx: &InferCtxt<'tcx>,
- impl_did: LocalDefId,
- span: Span,
- ty: Unnormalized<'tcx, Ty<'tcx>>,
-) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> {
- let ocx = ObligationCtxt::new(infcx);
- let Ok(normalized_ty) = ocx.structurally_normalize_ty(
- &traits::ObligationCause::misc(span, impl_did),
- tcx.param_env(impl_did),
- ty,
- ) else {
- // We shouldn't have errors here in the old solver, except for
- // evaluate/fulfill mismatches, but that's not a reason for an ICE.
- return None;
- };
- let errors = ocx.try_evaluate_obligations();
- if !errors.is_empty() {
- if infcx.next_trait_solver() {
- unreachable!();
- }
- // We shouldn't have errors here in the old solver, except for
- // evaluate/fulfill mismatches, but that's not a reason for an ICE.
- debug!(?errors, "encountered errors while fulfilling");
- return None;
- }
-
- Some((normalized_ty, ocx.into_pending_obligations()))
-}
-
pub(crate) fn reborrow_info<'tcx>(
tcx: TyCtxt<'tcx>,
impl_did: LocalDefId,
@@ -552,8 +521,12 @@ pub(crate) fn reborrow_info<'tcx>(
return Ok(());
}
+ let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
// We've found some data fields. They must all be either be Copy or Reborrow.
for (field, span) in data_fields {
+ let field = ocx
+ .deeply_normalize(&traits::ObligationCause::misc(span, impl_did), param_env, field)
+ .map_err(|errors| infcx.err_ctxt().report_fulfillment_errors(errors))?;
if assert_field_type_is_reborrow(
tcx,
&infcx,
@@ -620,17 +593,16 @@ pub(crate) fn coerce_shared_info<'tcx>(
}
assert_eq!(trait_ref.def_id, coerce_shared_trait);
- let Some((target, _obligations)) = structurally_normalize_ty(
- tcx,
- &infcx,
- impl_did,
- span,
- Unnormalized::new_wip(trait_ref.args.type_at(1)),
- ) else {
- todo!("something went wrong with structurally_normalize_ty");
- };
-
+ let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let param_env = tcx.param_env(impl_did);
+ let (source, target) = ocx
+ .deeply_normalize(
+ &traits::ObligationCause::misc(span, impl_did),
+ param_env,
+ Unnormalized::new_wip((source, trait_ref.args.type_at(1))),
+ )
+ .map_err(|errors| infcx.err_ctxt().report_fulfillment_errors(errors))?;
+
assert!(!source.has_escaping_bound_vars());
let data = match (source.kind(), target.kind()) {
@@ -672,6 +644,20 @@ pub(crate) fn coerce_shared_info<'tcx>(
// them below.
let (a, span_a) = a_data_fields[0];
let (b, span_b) = b_data_fields[0];
+ let a = ocx
+ .deeply_normalize(
+ &traits::ObligationCause::misc(span_a, impl_did),
+ param_env,
+ a,
+ )
+ .map_err(|errors| infcx.err_ctxt().report_fulfillment_errors(errors))?;
+ let b = ocx
+ .deeply_normalize(
+ &traits::ObligationCause::misc(span_b, impl_did),
+ param_env,
+ b,
+ )
+ .map_err(|errors| infcx.err_ctxt().report_fulfillment_errors(errors))?;
Some((a, b, coerce_shared_trait, span_a, span_b))
} else {
@@ -696,12 +682,19 @@ pub(crate) fn coerce_shared_info<'tcx>(
//
// 1 data field each; they must be the same type and Copy, or relate to one another using
// CoerceShared.
+ //
+ // FIXME(reborrow): we should do the relating inside `probe` so the region constraint
+ // doesn't affect later result in case that this relating fails.
+ // We should resolve regions if the relating succeeds.
+ // Besides, the regions of `Ref`s are not checked here so `&'a mut T -> &'static T` is
+ // allowed.
if source.ref_mutability() == Some(ty::Mutability::Mut)
&& target.ref_mutability() == Some(ty::Mutability::Not)
&& infcx
- .eq_structurally_relating_aliases(
+ .relate(
param_env,
source.peel_refs(),
+ ty::Variance::Invariant,
target.peel_refs(),
source_field_span,
)
@@ -710,14 +703,16 @@ pub(crate) fn coerce_shared_info<'tcx>(
// &mut T implements CoerceShared to &T, except not really.
return Ok(());
}
+
+ // FIXME(reborrow): we should do the relating inside `probe` so the region constraint
+ // doesn't affect later result in case that this relating fails.
if infcx
- .eq_structurally_relating_aliases(param_env, source, target, source_field_span)
+ .relate(param_env, source, ty::Variance::Invariant, target, source_field_span)
.is_err()
{
// The two data fields don't agree on a common type; this means
// that they must be `A: CoerceShared`. Register an obligation
// for that.
- let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let cause = traits::ObligationCause::misc(span, impl_did);
let obligation = Obligation::new(
tcx,
@@ -735,6 +730,8 @@ pub(crate) fn coerce_shared_info<'tcx>(
ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;
} else {
// Types match: check that it is Copy.
+ //
+ // FIXME(reborrow): We should resolve regions here.
assert_field_type_is_copy(tcx, &infcx, impl_did, param_env, source, source_field_span)?;
}
}
@@ -754,19 +751,20 @@ fn generic_lifetime_params_count(args: &[ty::GenericArg<'_>]) -> usize {
args.iter().filter(|arg| arg.as_region().is_some()).count()
}
-// FIXME(#155345): This should return `Unnormalized`
fn collect_struct_data_fields<'tcx>(
tcx: TyCtxt<'tcx>,
def: ty::AdtDef<'tcx>,
args: ty::GenericArgsRef<'tcx>,
-) -> Vec<(Ty<'tcx>, Span)> {
+) -> Vec<(Unnormalized<'tcx, Ty<'tcx>>, Span)> {
def.non_enum_variant()
.fields
.iter()
.filter_map(|f| {
// Ignore PhantomData fields
- let ty = f.ty(tcx, args).skip_norm_wip();
- if ty.is_phantom_data() {
+ let ty = f.ty(tcx, args);
+ // FIXME(#155345): alias might be normalized to PhantomData.
+ // We probably should normalize here instead.
+ if ty.skip_norm_wip().is_phantom_data() {
return None;
}
Some((ty, tcx.def_span(f.did)))
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index c3bd09f2a4767..1486c6c5e2753 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -97,7 +97,13 @@ pub(crate) fn orphan_check_impl(
);
if tcx.trait_is_auto(trait_def_id) {
- let self_ty = trait_ref.self_ty();
+ // Expand free alias types (e.g. lazy type aliases) so that the checks below operate
+ // on the type the alias resolves to rather than the alias itself. Otherwise an impl
+ // whose self type is an alias expanding to an otherwise valid nominal type would be
+ // wrongly rejected, e.g. `unsafe impl Sync for Alias {}` where `type Alias = Local;`.
+ // Expansion also reveals genuinely problematic self types (trait objects, opaque
+ // types, type parameters), so those keep being rejected. See issue #157756.
+ let self_ty = tcx.expand_free_alias_tys(trait_ref.self_ty());
// If the impl is in the same crate as the auto-trait, almost anything
// goes.
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index a3264d3cc3311..640f31355ac33 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -788,6 +788,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(annotate_moves, AnnotateMoves::Enabled(Some(1234)));
tracked!(assume_incomplete_release, true);
tracked!(autodiff, vec![AutoDiff::Enable, AutoDiff::NoTT]);
+ tracked!(autodiff_post_passes, Some("function(mem2reg,instsimplify,simplifycfg)".to_string()));
tracked!(binary_dep_depinfo, true);
tracked!(box_noalias, false);
tracked!(
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index 45229e5399dd5..fe5b8edce4a1d 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -616,6 +616,7 @@ extern "C" LLVMRustResult LLVMRustOptimize(
bool DebugInfoForProfiling, void *LlvmSelfProfiler,
LLVMRustSelfProfileBeforePassCallback BeforePassCallback,
LLVMRustSelfProfileAfterPassCallback AfterPassCallback,
+ const char *PostEnzymePasses, size_t PostEnzymePassesLen,
const char *ExtraPasses, size_t ExtraPassesLen, const char *LLVMPlugins,
size_t LLVMPluginsLen) {
Module *TheModule = unwrap(ModuleRef);
@@ -852,120 +853,130 @@ extern "C" LLVMRustResult LLVMRustOptimize(
raw_string_ostream ThinLinkDataOS(ThinLTOSummaryBuffer->data);
bool IsLTO = OptStage == LLVMRustOptStage::ThinLTO ||
OptStage == LLVMRustOptStage::FatLTO;
- if (!NoPrepopulatePasses) {
- for (const auto &C : PipelineStartEPCallbacks)
- PB.registerPipelineStartEPCallback(C);
- for (const auto &C : OptimizerLastEPCallbacks)
- PB.registerOptimizerLastEPCallback(C);
-
- // The pre-link pipelines don't support O0 and require using
- // buildO0DefaultPipeline() instead. At the same time, the LTO pipelines do
- // support O0 and using them is required.
- if (OptLevel == OptimizationLevel::O0 && !IsLTO) {
- // We manually schedule ThinLTOBufferPasses below, so don't pass the value
- // to enable it here.
- MPM = PB.buildO0DefaultPipeline(OptLevel);
- } else {
- switch (OptStage) {
- case LLVMRustOptStage::PreLinkNoLTO:
- if (ThinLTOBufferRef) {
- // This is similar to LLVM's `buildFatLTODefaultPipeline`, where the
- // bitcode for embedding is obtained after performing
- // `ThinLTOPreLinkDefaultPipeline`.
- MPM.addPass(PB.buildThinLTOPreLinkDefaultPipeline(OptLevel));
- MPM.addPass(ThinLTOBitcodeWriterPass(
- ThinLTODataOS,
- ThinLTOSummaryBufferRef ? &ThinLinkDataOS : nullptr));
- *ThinLTOBufferRef = ThinLTOBuffer.release();
- if (ThinLTOSummaryBufferRef) {
- *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release();
+ if (PostEnzymePassesLen) {
+ if (auto Err = PB.parsePassPipeline(
+ MPM, StringRef(PostEnzymePasses, PostEnzymePassesLen))) {
+ std::string ErrMsg = toString(std::move(Err));
+ LLVMRustSetLastError(ErrMsg.c_str());
+ return LLVMRustResult::Failure;
+ }
+ } else {
+ if (!NoPrepopulatePasses) {
+ for (const auto &C : PipelineStartEPCallbacks)
+ PB.registerPipelineStartEPCallback(C);
+ for (const auto &C : OptimizerLastEPCallbacks)
+ PB.registerOptimizerLastEPCallback(C);
+
+ // The pre-link pipelines don't support O0 and require using
+ // buildO0DefaultPipeline() instead. At the same time, the LTO pipelines
+ // do support O0 and using them is required.
+ if (OptLevel == OptimizationLevel::O0 && !IsLTO) {
+ // We manually schedule ThinLTOBufferPasses below, so don't pass the
+ // value to enable it here.
+ MPM = PB.buildO0DefaultPipeline(OptLevel);
+ } else {
+ switch (OptStage) {
+ case LLVMRustOptStage::PreLinkNoLTO:
+ if (ThinLTOBufferRef) {
+ // This is similar to LLVM's `buildFatLTODefaultPipeline`, where the
+ // bitcode for embedding is obtained after performing
+ // `ThinLTOPreLinkDefaultPipeline`.
+ MPM.addPass(PB.buildThinLTOPreLinkDefaultPipeline(OptLevel));
+ MPM.addPass(ThinLTOBitcodeWriterPass(
+ ThinLTODataOS,
+ ThinLTOSummaryBufferRef ? &ThinLinkDataOS : nullptr));
+ *ThinLTOBufferRef = ThinLTOBuffer.release();
+ if (ThinLTOSummaryBufferRef) {
+ *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release();
+ }
+ MPM.addPass(PB.buildModuleOptimizationPipeline(
+ OptLevel, ThinOrFullLTOPhase::None));
+ MPM.addPass(
+ createModuleToFunctionPassAdaptor(AnnotationRemarksPass()));
+ } else {
+ MPM = PB.buildPerModuleDefaultPipeline(OptLevel);
}
- MPM.addPass(PB.buildModuleOptimizationPipeline(
- OptLevel, ThinOrFullLTOPhase::None));
- MPM.addPass(
- createModuleToFunctionPassAdaptor(AnnotationRemarksPass()));
- } else {
- MPM = PB.buildPerModuleDefaultPipeline(OptLevel);
+ break;
+ case LLVMRustOptStage::PreLinkThinLTO:
+ case LLVMRustOptStage::PreLinkFatLTO:
+ MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel);
+ NeedThinLTOBufferPasses = false;
+ break;
+ case LLVMRustOptStage::ThinLTO:
+ // FIXME: Does it make sense to pass the ModuleSummaryIndex?
+ // It only seems to be needed for C++ specific optimizations.
+ MPM = PB.buildThinLTODefaultPipeline(OptLevel, nullptr);
+ break;
+ case LLVMRustOptStage::FatLTO:
+ MPM = PB.buildLTODefaultPipeline(OptLevel, nullptr);
+ NeedThinLTOBufferPasses = false;
+ break;
}
- break;
- case LLVMRustOptStage::PreLinkThinLTO:
- case LLVMRustOptStage::PreLinkFatLTO:
- MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel);
- NeedThinLTOBufferPasses = false;
- break;
- case LLVMRustOptStage::ThinLTO:
- // FIXME: Does it make sense to pass the ModuleSummaryIndex?
- // It only seems to be needed for C++ specific optimizations.
- MPM = PB.buildThinLTODefaultPipeline(OptLevel, nullptr);
- break;
- case LLVMRustOptStage::FatLTO:
- MPM = PB.buildLTODefaultPipeline(OptLevel, nullptr);
- NeedThinLTOBufferPasses = false;
- break;
}
+ } else {
+ // We're not building any of the default pipelines but we still want to
+ // add the verifier, instrumentation, etc passes if they were requested
+ for (const auto &C : PipelineStartEPCallbacks)
+ C(MPM, OptLevel);
+ for (const auto &C : OptimizerLastEPCallbacks)
+ C(MPM, OptLevel, ThinOrFullLTOPhase::None);
}
- } else {
- // We're not building any of the default pipelines but we still want to
- // add the verifier, instrumentation, etc passes if they were requested
- for (const auto &C : PipelineStartEPCallbacks)
- C(MPM, OptLevel);
- for (const auto &C : OptimizerLastEPCallbacks)
- C(MPM, OptLevel, ThinOrFullLTOPhase::None);
- }
- if (ExtraPassesLen) {
- if (auto Err =
- PB.parsePassPipeline(MPM, StringRef(ExtraPasses, ExtraPassesLen))) {
- std::string ErrMsg = toString(std::move(Err));
- LLVMRustSetLastError(ErrMsg.c_str());
- return LLVMRustResult::Failure;
+ if (ExtraPassesLen) {
+ if (auto Err = PB.parsePassPipeline(
+ MPM, StringRef(ExtraPasses, ExtraPassesLen))) {
+ std::string ErrMsg = toString(std::move(Err));
+ LLVMRustSetLastError(ErrMsg.c_str());
+ return LLVMRustResult::Failure;
+ }
}
- }
- if (NeedThinLTOBufferPasses) {
- MPM.addPass(CanonicalizeAliasesPass());
- MPM.addPass(NameAnonGlobalPass());
- }
- // For `-Copt-level=0`, and the pre-link fat/thin LTO stages.
- if (ThinLTOBufferRef && *ThinLTOBufferRef == nullptr) {
- // thin lto summaries prevent fat lto, so do not emit them if fat
- // lto is requested. See PR #136840 for background information.
- if (OptStage != LLVMRustOptStage::PreLinkFatLTO) {
- MPM.addPass(ThinLTOBitcodeWriterPass(
- ThinLTODataOS, ThinLTOSummaryBufferRef ? &ThinLinkDataOS : nullptr));
- } else {
- MPM.addPass(BitcodeWriterPass(ThinLTODataOS));
+ if (NeedThinLTOBufferPasses) {
+ MPM.addPass(CanonicalizeAliasesPass());
+ MPM.addPass(NameAnonGlobalPass());
}
- *ThinLTOBufferRef = ThinLTOBuffer.release();
- if (ThinLTOSummaryBufferRef) {
- *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release();
+ // For `-Copt-level=0`, and the pre-link fat/thin LTO stages.
+ if (ThinLTOBufferRef && *ThinLTOBufferRef == nullptr) {
+ // thin lto summaries prevent fat lto, so do not emit them if fat
+ // lto is requested. See PR #136840 for background information.
+ if (OptStage != LLVMRustOptStage::PreLinkFatLTO) {
+ MPM.addPass(ThinLTOBitcodeWriterPass(
+ ThinLTODataOS,
+ ThinLTOSummaryBufferRef ? &ThinLinkDataOS : nullptr));
+ } else {
+ MPM.addPass(BitcodeWriterPass(ThinLTODataOS));
+ }
+ *ThinLTOBufferRef = ThinLTOBuffer.release();
+ if (ThinLTOSummaryBufferRef) {
+ *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release();
+ }
}
- }
- // now load "-enzyme" pass:
- // With dlopen, ENZYME macro may not be defined, so check EnzymePtr directly
- // In the case of debug builds with multiple codegen units, we might not
- // have all function definitions available during the early compiler
- // invocations. We therefore wait for the final lto step to run Enzyme.
- if (EnzymePtr && IsLTO) {
-
- if (PrintBeforeEnzyme) {
- // Handle the Rust flag `-Zautodiff=PrintModBefore`.
- std::string Banner = "Module before EnzymeNewPM";
- MPM.addPass(PrintModulePass(outs(), Banner, true, false));
- }
+ // now load "-enzyme" pass:
+ // With dlopen, ENZYME macro may not be defined, so check EnzymePtr directly
+ // In the case of debug builds with multiple codegen units, we might not
+ // have all function definitions available during the early compiler
+ // invocations. We therefore wait for the final lto step to run Enzyme.
+ if (EnzymePtr && IsLTO) {
+
+ if (PrintBeforeEnzyme) {
+ // Handle the Rust flag `-Zautodiff=PrintModBefore`.
+ std::string Banner = "Module before EnzymeNewPM";
+ MPM.addPass(PrintModulePass(outs(), Banner, true, false));
+ }
- EnzymePtr(PB, false);
- if (auto Err = PB.parsePassPipeline(MPM, "enzyme")) {
- std::string ErrMsg = toString(std::move(Err));
- LLVMRustSetLastError(ErrMsg.c_str());
- return LLVMRustResult::Failure;
- }
+ EnzymePtr(PB, false);
+ if (auto Err = PB.parsePassPipeline(MPM, "enzyme")) {
+ std::string ErrMsg = toString(std::move(Err));
+ LLVMRustSetLastError(ErrMsg.c_str());
+ return LLVMRustResult::Failure;
+ }
- if (PrintAfterEnzyme) {
- // Handle the Rust flag `-Zautodiff=PrintModAfter`.
- std::string Banner = "Module after EnzymeNewPM";
- MPM.addPass(PrintModulePass(outs(), Banner, true, false));
+ if (PrintAfterEnzyme) {
+ // Handle the Rust flag `-Zautodiff=PrintModAfter`.
+ std::string Banner = "Module after EnzymeNewPM";
+ MPM.addPass(PrintModulePass(outs(), Banner, true, false));
+ }
}
}
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 91ff3f52390e1..dd1f9a1a5e1c9 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -306,9 +306,9 @@ pub enum StatementKind<'tcx> {
/// Assign statements roughly correspond to an assignment in Rust proper (`x = ...`) except
/// without the possibility of dropping the previous value (that must be done separately, if at
/// all). The *exact* way this works is undecided. It probably does something like evaluating
- /// the LHS to a place and the RHS to a value, and then storing the value to the place. Various
- /// parts of this may do type specific things that are more complicated than simply copying
- /// bytes. In particular, the assignment will typically erase the contents of padding,
+ /// the LHS to a place, then the RHS to a value, and then storing the value to the place.
+ /// Various parts of this may do type specific things that are more complicated than simply
+ /// copying bytes. In particular, the assignment will typically erase the contents of padding,
/// erase provenance from non-pointer types, and implicitly "retag" all references and boxes
/// that it copies, meaning that the resulting value is not an exact duplicate for all intents
/// and purposes of the original value.
@@ -789,6 +789,9 @@ pub enum TerminatorKind<'tcx> {
/// operands not aliasing the return place. It is unclear how this is justified in MIR, see
/// [#71117].
///
+ /// The evaluation order is currently "first compute destination place, then `func` operand,
+ /// then the arguments in left-to-right order".
+ ///
/// [#71117]: https://github.com/rust-lang/rust/issues/71117
Call {
/// The function that’s being called.
@@ -1511,7 +1514,7 @@ pub enum CastKind {
/// but running a transmute between differently-sized types is UB.
Transmute,
- /// A `Subtype` cast is applied to any `StatementKind::Assign` where
+ /// A `Subtype` cast is applied to any [`StatementKind::Assign`] where
/// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping
/// explicit during optimizations and codegen.
///
diff --git a/compiler/rustc_mir_build/src/builder/block.rs b/compiler/rustc_mir_build/src/builder/block.rs
index 47d231603d33b..7d85579325751 100644
--- a/compiler/rustc_mir_build/src/builder/block.rs
+++ b/compiler/rustc_mir_build/src/builder/block.rs
@@ -184,7 +184,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Declare the bindings, which may create a source scope.
let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree);
- this.push_scope((*remainder_scope, source_info));
+ this.push_scope(*remainder_scope);
let_scope_stack.push(remainder_scope);
let visibility_scope =
@@ -242,7 +242,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.block_context.push(BlockFrame::Statement { ignores_expr_result });
// Enter the remainder scope, i.e., the bindings' destruction scope.
- this.push_scope((*remainder_scope, source_info));
+ this.push_scope(*remainder_scope);
let_scope_stack.push(remainder_scope);
// Declare the bindings, which may create a source scope.
@@ -346,7 +346,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Finally, we pop all the let scopes before exiting out from the scope of block
// itself.
for scope in let_scope_stack.into_iter().rev() {
- block = this.pop_scope((*scope, source_info), block).into_block();
+ block = this.pop_scope(*scope, block).into_block();
}
// Restore the original source scope.
this.source_scope = outer_source_scope;
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
index dd36411658305..ea484bd05878b 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
@@ -726,7 +726,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// This can be `None` if the expression's temporary scope was extended so that it can be
// borrowed by a `const` or `static`. In that case, it's never dropped.
if let Some(temp_lifetime) = temp_lifetime {
- this.schedule_drop_storage_and_value(upvar_span, temp_lifetime, temp);
+ this.schedule_drop_storage(upvar_span, temp_lifetime, temp);
+ this.schedule_drop_value(upvar_span, temp_lifetime, temp);
}
block.and(Operand::Move(Place::from(temp)))
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_temp.rs b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs
index 55296c647c819..80d0e061487a7 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_temp.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs
@@ -7,7 +7,7 @@ use rustc_middle::mir::*;
use rustc_middle::thir::*;
use tracing::{debug, instrument};
-use crate::builder::scope::{DropKind, LintLevel};
+use crate::builder::scope::LintLevel;
use crate::builder::{BlockAnd, BlockAndExtension, Builder};
impl<'a, 'tcx> Builder<'a, 'tcx> {
@@ -120,7 +120,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// `bar(&foo())` or anything within a block will keep the
// regular drops just like runtime code.
if let Some(temp_lifetime) = temp_lifetime.temp_lifetime {
- this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage);
+ this.schedule_drop_storage(expr_span, temp_lifetime, temp);
}
}
}
@@ -128,7 +128,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = this.expr_into_dest(temp_place, block, expr_id).into_block();
if let Some(temp_lifetime) = temp_lifetime.temp_lifetime {
- this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);
+ this.schedule_drop_value(expr_span, temp_lifetime, temp);
}
if let Some(backwards_incompatible) = temp_lifetime.backwards_incompatible {
diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs
index f70416b2cf867..109f4de2698a4 100644
--- a/compiler/rustc_mir_build/src/builder/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs
@@ -28,7 +28,7 @@ use crate::builder::ForGuard::{self, OutsideGuard, RefWithinGuard};
use crate::builder::expr::as_place::PlaceBuilder;
use crate::builder::matches::buckets::PartitionedCandidates;
use crate::builder::matches::user_ty::ProjectedUserTypesNode;
-use crate::builder::scope::{DropKind, LintLevel};
+use crate::builder::scope::LintLevel;
use crate::builder::{
BlockAnd, BlockAndExtension, Builder, GuardFrame, GuardFrameLocal, LocalsForNode,
};
@@ -791,7 +791,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id)
&& matches!(schedule_drop, ScheduleDrops::Yes)
{
- self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
+ self.schedule_drop_storage(span, region_scope, local_id);
}
let local_info = self.local_decls[local_id].local_info.as_mut().unwrap_crate_local();
if let LocalInfo::User(BindingForm::Var(var_info)) = &mut **local_info {
@@ -808,7 +808,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) {
let local_id = self.var_local_id(var, for_guard);
if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) {
- self.schedule_drop(span, region_scope, local_id, DropKind::Value);
+ self.schedule_drop_value(span, region_scope, local_id);
}
}
diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index 378e2e618fb93..b6478fc9c8deb 100644
--- a/compiler/rustc_mir_build/src/builder/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -42,7 +42,7 @@ use rustc_middle::{bug, span_bug};
use rustc_span::{Span, Symbol};
use crate::builder::expr::as_place::PlaceBuilder;
-use crate::builder::scope::{DropKind, LintLevel};
+use crate::builder::scope::LintLevel;
pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
tcx: TyCtxt<'tcx>,
@@ -955,11 +955,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let place = Place::from(local);
// Make sure we drop (parts of) the argument even when not matched on.
- self.schedule_drop(
+ self.schedule_drop_value(
param.pat.as_ref().map_or(expr_span, |pat| pat.span),
argument_scope,
local,
- DropKind::Value,
);
let Some(ref pat) = param.pat else {
diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs
index 84abb6ca70e70..2197bbeab25cf 100644
--- a/compiler/rustc_mir_build/src/builder/scope.rs
+++ b/compiler/rustc_mir_build/src/builder/scope.rs
@@ -162,7 +162,7 @@ struct DropData {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(crate) enum DropKind {
+enum DropKind {
Value,
Storage,
ForLint,
@@ -488,11 +488,11 @@ impl<'tcx> Scopes<'tcx> {
}
}
- fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo), vis_scope: SourceScope) {
+ fn push_scope(&mut self, region_scope: region::Scope, vis_scope: SourceScope) {
debug!("push_scope({:?})", region_scope);
self.scopes.push(Scope {
source_scope: vis_scope,
- region_scope: region_scope.0,
+ region_scope,
drops: vec![],
moved_locals: vec![],
cached_unwind_block: None,
@@ -500,13 +500,13 @@ impl<'tcx> Scopes<'tcx> {
});
}
- fn pop_scope(&mut self, region_scope: (region::Scope, SourceInfo)) -> Scope {
+ fn pop_scope(&mut self, region_scope: region::Scope) {
let scope = self.scopes.pop().unwrap();
- assert_eq!(scope.region_scope, region_scope.0);
- scope
+ assert_eq!(scope.region_scope, region_scope);
}
- fn scope_index(&self, region_scope: region::Scope, span: Span) -> usize {
+ /// Returns the position in the scope stack of `region_scope`.
+ fn stack_index(&self, region_scope: region::Scope, span: Span) -> usize {
self.scopes
.iter()
.rposition(|scope| scope.region_scope == region_scope)
@@ -681,7 +681,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
#[instrument(skip(self, f), level = "debug")]
pub(crate) fn in_scope(
&mut self,
- region_scope: (region::Scope, SourceInfo),
+ (region_scope, source_info): (region::Scope, SourceInfo),
lint_level: LintLevel,
f: F,
) -> BlockAnd
@@ -692,7 +692,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if let LintLevel::Explicit(current_hir_id) = lint_level {
let parent_id =
self.source_scopes[source_scope].local_data.as_ref().unwrap_crate_local().lint_root;
- self.maybe_new_source_scope(region_scope.1.span, current_hir_id, parent_id);
+ self.maybe_new_source_scope(source_info.span, current_hir_id, parent_id);
}
self.push_scope(region_scope);
let mut block;
@@ -721,7 +721,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// scope and call `pop_scope` afterwards. Note that these two
/// calls must be paired; using `in_scope` as a convenience
/// wrapper maybe preferable.
- pub(crate) fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo)) {
+ pub(crate) fn push_scope(&mut self, region_scope: region::Scope) {
self.scopes.push_scope(region_scope, self.source_scope);
}
@@ -730,13 +730,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// This must match 1-to-1 with `push_scope`.
pub(crate) fn pop_scope(
&mut self,
- region_scope: (region::Scope, SourceInfo),
+ region_scope: region::Scope,
mut block: BasicBlock,
) -> BlockAnd<()> {
debug!("pop_scope({:?}, {:?})", region_scope, block);
block = self.leave_top_scope(block);
-
self.scopes.pop_scope(region_scope);
block.unit()
@@ -804,7 +803,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
let region_scope = self.scopes.breakable_scopes[break_index].region_scope;
- let scope_index = self.scopes.scope_index(region_scope, span);
+ let stack_index = self.scopes.stack_index(region_scope, span);
let drops = if destination.is_some() {
&mut self.scopes.breakable_scopes[break_index].break_drops
} else {
@@ -822,7 +821,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
};
let mut drop_idx = ROOT_NODE;
- for scope in &self.scopes.scopes[scope_index + 1..] {
+ for scope in &self.scopes.scopes[stack_index + 1..] {
for drop in &scope.drops {
drop_idx = drops.add_drop(*drop, drop_idx);
}
@@ -1007,9 +1006,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.block_context.pop();
let discr = self.temp(discriminant_ty, source_info.span);
- let scope_index = self
+ let stack_index = self
.scopes
- .scope_index(self.scopes.const_continuable_scopes[break_index].region_scope, span);
+ .stack_index(self.scopes.const_continuable_scopes[break_index].region_scope, span);
let scope = &mut self.scopes.const_continuable_scopes[break_index];
self.cfg.push_assign(block, source_info, discr, rvalue);
let drop_and_continue_block = self.cfg.start_new_block();
@@ -1022,7 +1021,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let drops = &mut scope.const_continue_drops;
- let drop_idx = self.scopes.scopes[scope_index + 1..]
+ let drop_idx = self.scopes.scopes[stack_index + 1..]
.iter()
.flat_map(|scope| &scope.drops)
.fold(ROOT_NODE, |drop_idx, &drop| drops.add_drop(drop, drop_idx));
@@ -1032,10 +1031,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.terminate(imaginary_target, source_info, TerminatorKind::UnwindResume);
let region_scope = scope.region_scope;
- let scope_index = self.scopes.scope_index(region_scope, span);
+ let stack_index = self.scopes.stack_index(region_scope, span);
let mut drops = DropTree::new();
- let drop_idx = self.scopes.scopes[scope_index + 1..]
+ let drop_idx = self.scopes.scopes[stack_index + 1..]
.iter()
.flat_map(|scope| &scope.drops)
.fold(ROOT_NODE, |drop_idx, &drop| drops.add_drop(drop, drop_idx));
@@ -1066,14 +1065,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.unwrap_or_else(|| span_bug!(source_info.span, "no if-then scope found"));
let target = if_then_scope.region_scope;
- let scope_index = self.scopes.scope_index(target, source_info.span);
+ let stack_index = self.scopes.stack_index(target, source_info.span);
// Upgrade `if_then_scope` to `&mut`.
let if_then_scope = self.scopes.if_then_scope.as_mut().expect("upgrading & to &mut");
let mut drop_idx = ROOT_NODE;
let drops = &mut if_then_scope.else_drops;
- for scope in &self.scopes.scopes[scope_index + 1..] {
+ for scope in &self.scopes.scopes[stack_index + 1..] {
for drop in &scope.drops {
drop_idx = drops.add_drop(*drop, drop_idx);
}
@@ -1390,47 +1389,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Scheduling drops
// ================
- pub(crate) fn schedule_drop_storage_and_value(
- &mut self,
- span: Span,
- region_scope: region::Scope,
- local: Local,
- ) {
- self.schedule_drop(span, region_scope, local, DropKind::Storage);
- self.schedule_drop(span, region_scope, local, DropKind::Value);
- }
-
/// Indicates that `place` should be dropped on exit from `region_scope`.
///
/// When called with `DropKind::Storage`, `place` shouldn't be the return
/// place, or a function parameter.
- pub(crate) fn schedule_drop(
+ fn schedule_drop(
&mut self,
span: Span,
region_scope: region::Scope,
local: Local,
drop_kind: DropKind,
) {
- let needs_drop = match drop_kind {
- DropKind::Value | DropKind::ForLint => {
- if !self.local_decls[local].ty.needs_drop(self.tcx, self.typing_env()) {
- return;
- }
- true
- }
- DropKind::Storage => {
- if local.index() <= self.arg_count {
- span_bug!(
- span,
- "`schedule_drop` called with body argument {:?} \
- but its storage does not require a drop",
- local,
- )
- }
- false
- }
- };
-
// When building drops, we try to cache chains of drops to reduce the
// number of `DropTree::add_drop` calls. This, however, means that
// whenever we add a drop into a scope which already had some entries
@@ -1477,7 +1446,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// path, we only need to invalidate the cache for drops that happen on
// the unwind or coroutine drop paths. This means that for
// non-coroutines we don't need to invalidate caches for `DropKind::Storage`.
- let invalidate_caches = needs_drop || self.coroutine.is_some();
+ let invalidate_caches = match drop_kind {
+ DropKind::Value | DropKind::ForLint => true,
+ DropKind::Storage => self.coroutine.is_some(),
+ };
for scope in self.scopes.scopes.iter_mut().rev() {
if invalidate_caches {
scope.invalidate_cache();
@@ -1501,6 +1473,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, local);
}
+ /// Indicates that `place` should be marked `StorageDead` on exit from `region_scope`.
+ ///
+ /// `place` must not be the return place, or a function parameter.
+ pub(crate) fn schedule_drop_storage(
+ &mut self,
+ span: Span,
+ region_scope: region::Scope,
+ local: Local,
+ ) {
+ if local.index() <= self.arg_count {
+ span_bug!(
+ span,
+ "`schedule_drop` called with body argument {:?} \
+ but its storage does not require a drop",
+ local,
+ )
+ }
+ self.schedule_drop(span, region_scope, local, DropKind::Storage);
+ }
+
+ /// Indicates that `place` should be dropped on exit from `region_scope`.
+ pub(crate) fn schedule_drop_value(
+ &mut self,
+ span: Span,
+ region_scope: region::Scope,
+ local: Local,
+ ) {
+ if !self.local_decls[local].ty.needs_drop(self.tcx, self.typing_env()) {
+ return;
+ }
+ self.schedule_drop(span, region_scope, local, DropKind::Value);
+ }
+
/// Schedule emission of a backwards incompatible drop lint hint.
/// Applicable only to temporary values for now.
#[instrument(level = "debug", skip(self))]
@@ -1512,28 +1517,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) {
// Note that we are *not* gating BIDs here on whether they have significant destructor.
// We need to know all of them so that we can capture potential borrow-checking errors.
- for scope in self.scopes.scopes.iter_mut().rev() {
- // Since we are inserting linting MIR statement, we have to invalidate the caches
- scope.invalidate_cache();
- if scope.region_scope == region_scope {
- let region_scope_span = region_scope.span(self.tcx, self.region_scope_tree);
- let scope_end = self.tcx.sess.source_map().end_point(region_scope_span);
-
- scope.drops.push(DropData {
- source_info: SourceInfo { span: scope_end, scope: scope.source_scope },
- local,
- kind: DropKind::ForLint,
- });
-
- return;
- }
- }
- span_bug!(
- span,
- "region scope {:?} not in scope to drop {:?} for linting",
- region_scope,
- local
- );
+ self.schedule_drop(span, region_scope, local, DropKind::ForLint);
}
/// Indicates that the "local operand" stored in `local` is
@@ -1611,7 +1595,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// It is possible to unwind to some ancestor scope if some drop panics as
/// the program breaks out of a if-then scope.
fn diverge_cleanup_target(&mut self, target_scope: region::Scope, span: Span) -> DropIdx {
- let target = self.scopes.scope_index(target_scope, span);
+ let target = self.scopes.stack_index(target_scope, span);
let (uncached_scope, mut cached_drop) = self.scopes.scopes[..=target]
.iter()
.enumerate()
@@ -1674,7 +1658,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.coroutine.is_some(),
"diverge_dropline_target is valid only for coroutine"
);
- let target = self.scopes.scope_index(target_scope, span);
+ let target = self.scopes.stack_index(target_scope, span);
let (uncached_scope, mut cached_drop) = self.scopes.scopes[..=target]
.iter()
.enumerate()
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index ceb044c102f69..bd608583d818c 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -995,11 +995,18 @@ fn visit_instance_use<'tcx>(
output.push(create_fn_mono_item(tcx, panic_instance, source));
}
} else if !intrinsic.must_be_overridden
- && !tcx.sess.replaced_intrinsics.contains(&intrinsic.name)
+ && (tcx.sess.opts.unstable_opts.force_intrinsic_fallback
+ || !tcx.sess.replaced_intrinsics.contains(&intrinsic.name))
{
// Codegen the fallback body of intrinsics with fallback bodies.
// We have to skip this otherwise as there's no body to codegen.
- // We also skip intrinsics the backend handles, to reduce monomorphizations.
+ //
+ // We also skip `replaced_intrinsics` which are always replaced by the backend and hence
+ // monomorphizing the fallback body would be pointless.
+ //
+ // However, when -Zforce-intrinsic-fallback is set (e.g. to test the fallback
+ // implementations) we ignore the optimization hint and do monomorphize
+ // the fallback body.
let instance = ty::Instance::new_raw(instance.def_id(), instance.args);
if tcx.should_codegen_locally(instance) {
output.push(create_fn_mono_item(tcx, instance, source));
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index bb9bf8976f672..c35dc02fb1de1 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -475,7 +475,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
}
fn resolve_visibility(&mut self, vis: &ast::Visibility) -> Visibility {
- match self.r.try_resolve_visibility(&self.parent_scope, vis, true) {
+ match self.r.try_resolve_visibility(&self.parent_scope, vis, false) {
Ok(vis) => vis,
Err(error) => {
self.r.delayed_vis_resolution_errors.push(DelayedVisResolutionError {
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index d5babe2e94a90..6bf30a470cf39 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -1154,6 +1154,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
resolution.as_ref().and_then(|r| r.non_glob_decl).filter(|b| Some(*b) != ignore_decl);
if let Some(finalize) = finalize {
+ // finalize implies that the module is fully expanded
+ assert!(!module.has_unexpanded_invocations());
return self.get_mut().finalize_module_binding(
ident,
orig_ident_span,
@@ -1218,6 +1220,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
resolution.as_ref().and_then(|r| r.glob_decl).filter(|b| Some(*b) != ignore_decl);
if let Some(finalize) = finalize {
+ // finalize implies that the module is fully expanded
+ assert!(!module.has_unexpanded_invocations());
return self.get_mut().finalize_module_binding(
ident,
orig_ident_span,
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 4e5b38d34b824..b198e583be333 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -2298,6 +2298,8 @@ options! {
`=LooseTypes`
`=Inline`
Multiple options can be combined with commas."),
+ autodiff_post_passes: Option = (None, parse_opt_string, [TRACKED],
+ "set llvm passes to run after enzyme (no passes run when it is empty)"),
#[rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field")]
binary_dep_depinfo: bool = (false, parse_bool, [TRACKED],
"include artifacts (sysroot, crate dependencies) used during compilation in dep-info \
@@ -2417,6 +2419,9 @@ options! {
fmt_debug: FmtDebug = (FmtDebug::Full, parse_fmt_debug, [TRACKED],
"how detailed `#[derive(Debug)]` should be. `full` prints types recursively, \
`shallow` prints only type names, `none` prints nothing and disables `{:?}`. (default: `full`)"),
+ force_intrinsic_fallback: bool = (false, parse_bool, [TRACKED],
+ "always use the fallback body of an intrinsic, if it has one, instead of lowering \
+ the intrinsic in the codegen backend (default: no)."),
force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED],
"force all crates to be `rustc_private` unstable (default: no)"),
function_return: FunctionReturn = (FunctionReturn::default(), parse_function_return, [TRACKED],
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 78f4690443525..b92712750705e 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -991,7 +991,7 @@ crate::target_spec_enum! {
/// On PowerPC only: build for SPE.
PowerPcSpe = "powerpc-spe",
/// On x86-32/64, aarch64, and S390x: do not use any FPU or SIMD registers for the ABI.
- Softfloat = "softfloat", "x86-softfloat",
+ Softfloat = "softfloat",
}
parse_error_type = "rustc abi";
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index b77782b09ef85..e205855e146f5 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -167,7 +167,7 @@ impl, E: TypeVisitable> TypeVisitable for
}
}
-impl> TypeVisitable for &T {
+impl + ?Sized> TypeVisitable for &T {
fn visit_with>(&self, visitor: &mut V) -> V::Result {
(**self).visit_with(visitor)
}
@@ -179,7 +179,7 @@ impl> TypeVisitable for Arc {
}
}
-impl> TypeVisitable for Box {
+impl + ?Sized> TypeVisitable for Box {
fn visit_with>(&self, visitor: &mut V) -> V::Result {
(**self).visit_with(visitor)
}
@@ -209,14 +209,14 @@ impl, const N: usize> TypeVisitable for Smal
// `TypeFoldable` isn't impl'd for `&[T]`. It doesn't make sense in the general
// case, because we can't return a new slice. But note that there are a couple
// of trivial impls of `TypeFoldable` for specific slice types elsewhere.
-impl> TypeVisitable for &[T] {
+impl> TypeVisitable for [T] {
fn visit_with>(&self, visitor: &mut V) -> V::Result {
walk_visitable_list!(visitor, self.iter());
V::Result::output()
}
}
-impl> TypeVisitable for Box<[T]> {
+impl> TypeVisitable for [T; N] {
fn visit_with>(&self, visitor: &mut V) -> V::Result {
walk_visitable_list!(visitor, self.iter());
V::Result::output()
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs
index 40e0d8cacfff1..58da047da8e20 100644
--- a/library/alloc/src/boxed.rs
+++ b/library/alloc/src/boxed.rs
@@ -218,6 +218,8 @@ mod iter;
/// [`ThinBox`] implementation.
mod thin;
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+pub use iter::BoxedArrayIntoIter;
#[unstable(feature = "thin_box", issue = "92791")]
pub use thin::ThinBox;
diff --git a/library/alloc/src/boxed/iter.rs b/library/alloc/src/boxed/iter.rs
index 90582aa49c6d7..0e6afcf062354 100644
--- a/library/alloc/src/boxed/iter.rs
+++ b/library/alloc/src/boxed/iter.rs
@@ -1,18 +1,19 @@
use core::async_iter::AsyncIterator;
-use core::iter::FusedIterator;
+use core::iter::{FusedIterator, TrustedLen};
+use core::num::NonZero;
use core::pin::Pin;
use core::slice;
use core::task::{Context, Poll};
-use crate::alloc::Allocator;
+use crate::alloc::{Allocator, Global};
#[cfg(not(no_global_oom_handling))]
use crate::borrow::Cow;
use crate::boxed::Box;
#[cfg(not(no_global_oom_handling))]
use crate::string::String;
-use crate::vec;
#[cfg(not(no_global_oom_handling))]
use crate::vec::Vec;
+use crate::{fmt, vec};
#[stable(feature = "rust1", since = "1.0.0")]
impl Iterator for Box {
@@ -192,3 +193,154 @@ impl<'a> FromIterator> for Box {
String::from_iter(iter).into_boxed_str()
}
}
+
+/// This implementation is required to make sure that the `Box<[I; N]>: IntoIterator`
+/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket.
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl !Iterator for Box<[I; N], A> {}
+
+/// This implementation is required to make sure that the `&Box<[I; N]>: IntoIterator`
+/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket.
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl<'a, const N: usize, I, A: Allocator> !Iterator for &'a Box<[I; N], A> {}
+
+/// This implementation is required to make sure that the `&mut Box<[I; N]>: IntoIterator`
+/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket.
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl<'a, const N: usize, I, A: Allocator> !Iterator for &'a mut Box<[I; N], A> {}
+
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl<'a, T, const N: usize, A: Allocator> IntoIterator for &'a Box<[T; N], A> {
+ type IntoIter = slice::Iter<'a, T>;
+ type Item = &'a T;
+ fn into_iter(self) -> slice::Iter<'a, T> {
+ self.iter()
+ }
+}
+
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl<'a, T, const N: usize, A: Allocator> IntoIterator for &'a mut Box<[T; N], A> {
+ type IntoIter = slice::IterMut<'a, T>;
+ type Item = &'a mut T;
+ fn into_iter(self) -> slice::IterMut<'a, T> {
+ self.iter_mut()
+ }
+}
+
+/// A by-value `Box<[T; N]>` iterator.
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+#[rustc_insignificant_dtor]
+pub struct BoxedArrayIntoIter {
+ // FIXME: make a more efficient implementation (without the need to store capacity)
+ inner: vec::IntoIter,
+}
+
+impl BoxedArrayIntoIter {
+ /// Returns an immutable slice of all elements that have not been yielded
+ /// yet.
+ #[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+ pub fn as_slice(&self) -> &[T] {
+ self.inner.as_slice()
+ }
+
+ /// Returns a mutable slice of all elements that have not been yielded yet.
+ #[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+ pub fn as_mut_slice(&mut self) -> &mut [T] {
+ self.inner.as_mut_slice()
+ }
+}
+
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl Iterator for BoxedArrayIntoIter {
+ type Item = T;
+ fn next(&mut self) -> Option {
+ self.inner.next()
+ }
+
+ fn size_hint(&self) -> (usize, Option) {
+ let len = self.len();
+ (len, Some(len))
+ }
+
+ #[inline]
+ fn fold(self, init: Acc, fold: Fold) -> Acc
+ where
+ Fold: FnMut(Acc, Self::Item) -> Acc,
+ {
+ self.inner.fold(init, fold)
+ }
+
+ fn count(self) -> usize {
+ self.len()
+ }
+
+ fn last(mut self) -> Option {
+ self.next_back()
+ }
+
+ fn advance_by(&mut self, n: usize) -> Result<(), NonZero> {
+ self.inner.advance_by(n)
+ }
+}
+
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl DoubleEndedIterator for BoxedArrayIntoIter {
+ fn next_back(&mut self) -> Option {
+ self.inner.next_back()
+ }
+
+ #[inline]
+ fn rfold(self, init: Acc, rfold: Fold) -> Acc
+ where
+ Fold: FnMut(Acc, Self::Item) -> Acc,
+ {
+ self.inner.rfold(init, rfold)
+ }
+
+ fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> {
+ self.inner.advance_back_by(n)
+ }
+}
+
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl ExactSizeIterator for BoxedArrayIntoIter {
+ fn len(&self) -> usize {
+ self.inner.len()
+ }
+
+ fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+}
+
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl FusedIterator for BoxedArrayIntoIter {}
+
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+unsafe impl TrustedLen for BoxedArrayIntoIter {}
+
+#[cfg(not(no_global_oom_handling))]
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl Clone for BoxedArrayIntoIter {
+ fn clone(&self) -> Self {
+ Self { inner: self.inner.clone() }
+ }
+}
+
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl fmt::Debug for BoxedArrayIntoIter {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Only print the elements that were not yielded yet: we cannot
+ // access the yielded elements anymore.
+ f.debug_tuple("BoxedArrayIntoIter").field(&self.as_slice()).finish()
+ }
+}
+
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl IntoIterator for Box<[T; N], A> {
+ type IntoIter = BoxedArrayIntoIter;
+ type Item = T;
+ fn into_iter(self) -> BoxedArrayIntoIter {
+ BoxedArrayIntoIter { inner: (self as Box<[T], A>).into_vec().into_iter() }
+ }
+}
diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs
new file mode 100644
index 0000000000000..6f392bba93ef4
--- /dev/null
+++ b/library/alloc/src/io/impls.rs
@@ -0,0 +1,22 @@
+use crate::boxed::Box;
+use crate::io::SizeHint;
+
+// =============================================================================
+// Forwarding implementations
+
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
+impl SizeHint for Box {
+ #[inline]
+ fn lower_bound(&self) -> usize {
+ SizeHint::lower_bound(&**self)
+ }
+
+ #[inline]
+ fn upper_bound(&self) -> Option {
+ SizeHint::upper_bound(&**self)
+ }
+}
+
+// =============================================================================
+// In-memory buffer implementations
diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs
index 58e5ffa39d0c4..86dc8c685a985 100644
--- a/library/alloc/src/io/mod.rs
+++ b/library/alloc/src/io/mod.rs
@@ -1,6 +1,7 @@
//! Traits, helpers, and type definitions for core I/O functionality.
mod error;
+mod impls;
#[unstable(feature = "raw_os_error_ty", issue = "107792")]
pub use core::io::RawOsError;
@@ -17,4 +18,4 @@ pub use core::io::{
};
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
-pub use core::io::{OsFunctions, chain, take};
+pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, take};
diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs
index b7fcecf11e059..ff3c9433ab58b 100644
--- a/library/alloc/src/vec/into_iter.rs
+++ b/library/alloc/src/vec/into_iter.rs
@@ -552,7 +552,7 @@ where
#[doc(hidden)]
#[unstable(issue = "none", feature = "std_internals")]
#[rustc_unsafe_specialization_marker]
-pub trait NonDrop {}
+trait NonDrop {}
// T: Copy as approximation for !Drop since get_unchecked does not advance self.ptr
// and thus we can't implement drop-handling
diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs
index 7aed4a1324060..0877b7bad9512 100644
--- a/library/core/src/array/iter.rs
+++ b/library/core/src/array/iter.rs
@@ -34,6 +34,9 @@ impl IntoIter {
}
}
+#[stable(feature = "boxed_array_value_iter", since = "CURRENT_RUSTC_VERSION")]
+impl !Iterator for [T; N] {}
+
// Note: the `#[rustc_skip_during_method_dispatch(array)]` on `trait IntoIterator`
// hides this implementation from explicit `.into_iter()` calls on editions < 2021,
// so those calls will still resolve to the slice implementation, by reference.
@@ -365,7 +368,7 @@ unsafe impl TrustedLen for IntoIter {}
#[doc(hidden)]
#[unstable(issue = "none", feature = "std_internals")]
#[rustc_unsafe_specialization_marker]
-pub trait NonDrop {}
+trait NonDrop {}
// T: Copy as approximation for !Drop since get_unchecked does not advance self.alive
// and thus we can't implement drop-handling
diff --git a/library/core/src/io/impls.rs b/library/core/src/io/impls.rs
new file mode 100644
index 0000000000000..cc222c56121e2
--- /dev/null
+++ b/library/core/src/io/impls.rs
@@ -0,0 +1,35 @@
+use crate::io::SizeHint;
+
+// =============================================================================
+// Forwarding implementations
+
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
+impl SizeHint for &mut T {
+ #[inline]
+ fn lower_bound(&self) -> usize {
+ SizeHint::lower_bound(*self)
+ }
+
+ #[inline]
+ fn upper_bound(&self) -> Option {
+ SizeHint::upper_bound(*self)
+ }
+}
+
+// =============================================================================
+// In-memory buffer implementations
+
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
+impl SizeHint for &[u8] {
+ #[inline]
+ fn lower_bound(&self) -> usize {
+ self.len()
+ }
+
+ #[inline]
+ fn upper_bound(&self) -> Option {
+ Some(self.len())
+ }
+}
diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs
index b3c8815cb062c..95fcc2d6dc19b 100644
--- a/library/core/src/io/mod.rs
+++ b/library/core/src/io/mod.rs
@@ -3,7 +3,9 @@
mod borrowed_buf;
mod cursor;
mod error;
+mod impls;
mod io_slice;
+mod size_hint;
mod util;
#[unstable(feature = "core_io_borrowed_buf", issue = "117693")]
@@ -25,5 +27,26 @@ pub use self::{
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub use self::{
error::{Custom, CustomOwner, OsFunctions},
+ size_hint::SizeHint,
util::{chain, take},
};
+
+/// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically
+/// implemented for handle types like [`Arc`][arc] as well.
+///
+/// This trait should only be implemented for types where `<&T as Trait>::method(&mut &value, ..)`
+/// would be identical to `::method(&mut value, ..)`.
+///
+/// [`File`][file] passes this test, as operations on `&File` and `File` both affect
+/// the same underlying file.
+/// `[u8]` fails, because any modification to `&mut &[u8]` would only affect a temporary
+/// and be lost after the method has been called.
+///
+// FIXME(#74481): Hard-links required to link from `core` to `std`
+/// [file]: ../../std/fs/struct.File.html
+/// [arc]: ../../alloc/sync/struct.Arc.html
+/// [`Write`]: ../../std/io/trait.Write.html
+/// [`Seek`]: ../../std/io/trait.Seek.html
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
+pub trait IoHandle {}
diff --git a/library/core/src/io/size_hint.rs b/library/core/src/io/size_hint.rs
new file mode 100644
index 0000000000000..fa4e25c62f6d0
--- /dev/null
+++ b/library/core/src/io/size_hint.rs
@@ -0,0 +1,60 @@
+/// Internal trait used to allow for specialization in `Read::size_hint` and
+/// `Iterator::size_hint`.
+///
+/// All types implement this through a blanket default implementation returning
+/// a hint of `(0, None)`.
+///
+/// Implementors should only provide [`lower_bound`](SizeHint::lower_bound) and
+/// [`upper_bound`](SizeHint::upper_bound).
+/// [`size_hint`](SizeHint::size_hint) is provided as a `final` method to enforce
+/// correctness.
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
+pub trait SizeHint {
+ /// Returns a lower bound on the number of elements this container-like item
+ /// contains.
+ /// For example, an array `[u8; 12]` could return any value between `0` and
+ /// `12` inclusively as a correct implementation.
+ ///
+ /// Through specialization, all types implement this method returning a default
+ /// value of `0`.
+ ///
+ /// Implementations *must* ensure the returned value is less than or equal to
+ /// the true element count.
+ fn lower_bound(&self) -> usize;
+
+ /// Returns an upper bound on the number of elements this container-like item
+ /// contains if it can be determined, otherwise `None`.
+ ///
+ /// Through specialization, all types implement this method returning a default
+ /// value of `None`.
+ ///
+ /// Implementations *must* ensure the returned value is greater than or equal
+ /// to the true element count.
+ fn upper_bound(&self) -> Option;
+
+ /// Returns an estimate for the number of elements this container like type
+ /// contains.
+ ///
+ /// This is a `final` method, and is guaranteed to return
+ /// `(self.lower_bound(), self.upper_bound())`.
+ ///
+ /// Without specialization, types implementing this trait will return `(0, None)`.
+ final fn size_hint(&self) -> (usize, Option) {
+ (self.lower_bound(), self.upper_bound())
+ }
+}
+
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
+impl SizeHint for T {
+ #[inline]
+ default fn lower_bound(&self) -> usize {
+ 0
+ }
+
+ #[inline]
+ default fn upper_bound(&self) -> Option {
+ None
+ }
+}
diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs
index 232e388433fb0..a40318bb85e08 100644
--- a/library/core/src/io/util.rs
+++ b/library/core/src/io/util.rs
@@ -1,4 +1,5 @@
-use crate::fmt;
+use crate::io::SizeHint;
+use crate::{cmp, fmt};
/// `Empty` ignores any data written via [`Write`], and will always be empty
/// (returning zero bytes) when read via [`Read`].
@@ -13,6 +14,15 @@ use crate::fmt;
#[derive(Copy, Clone, Debug, Default)]
pub struct Empty;
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
+impl SizeHint for Empty {
+ #[inline]
+ fn upper_bound(&self) -> Option {
+ Some(0)
+ }
+}
+
/// Creates a value that is always at EOF for reads, and ignores all data written.
///
/// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`]
@@ -63,6 +73,20 @@ pub struct Repeat {
pub byte: u8,
}
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
+impl SizeHint for Repeat {
+ #[inline]
+ fn lower_bound(&self) -> usize {
+ usize::MAX
+ }
+
+ #[inline]
+ fn upper_bound(&self) -> Option {
+ None
+ }
+}
+
/// Creates an instance of a reader that infinitely repeats one byte.
///
/// All reads from this reader will succeed by filling the specified buffer with
@@ -145,6 +169,23 @@ pub struct Chain {
pub done_first: bool,
}
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
+impl SizeHint for Chain {
+ #[inline]
+ fn lower_bound(&self) -> usize {
+ SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second)
+ }
+
+ #[inline]
+ fn upper_bound(&self) -> Option {
+ match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) {
+ (Some(first), Some(second)) => first.checked_add(second),
+ _ => None,
+ }
+ }
+}
+
impl Chain {
/// Consumes the `Chain`, returning the wrapped readers.
///
@@ -253,6 +294,23 @@ pub struct Take {
pub limit: u64,
}
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
+impl SizeHint for Take {
+ #[inline]
+ fn lower_bound(&self) -> usize {
+ cmp::min(SizeHint::lower_bound(&self.inner) as u64, self.limit) as usize
+ }
+
+ #[inline]
+ fn upper_bound(&self) -> Option {
+ match SizeHint::upper_bound(&self.inner) {
+ Some(upper_bound) => Some(cmp::min(upper_bound as u64, self.limit) as usize),
+ None => self.limit.try_into().ok(),
+ }
+ }
+}
+
impl Take {
/// Returns the number of bytes that can be read before this instance will
/// return EOF.
diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs
index a3e5ae5e7698b..6c502ee75e17f 100644
--- a/library/core/src/iter/adapters/array_chunks.rs
+++ b/library/core/src/iter/adapters/array_chunks.rs
@@ -1,8 +1,6 @@
use crate::array;
use crate::iter::adapters::SourceIter;
-use crate::iter::{
- ByRefSized, FusedIterator, InPlaceIterable, TrustedFused, TrustedRandomAccessNoCoerce,
-};
+use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused, TrustedRandomAccessNoCoerce};
use crate::num::NonZero;
use crate::ops::{ControlFlow, NeverShortCircuit, Try};
@@ -128,15 +126,11 @@ where
self.next_back_remainder();
let mut acc = init;
- let mut iter = ByRefSized(&mut self.iter).rev();
// NB remainder is handled by `next_back_remainder`, so
- // `next_chunk` can't return `Err` with non-empty remainder
+ // `next_chunk_back` can't return `Err` with non-empty remainder
// (assuming correct `I as ExactSizeIterator` impl).
- while let Ok(mut chunk) = iter.next_chunk() {
- // FIXME: do not do double reverse
- // (we could instead add `next_chunk_back` for example)
- chunk.reverse();
+ while let Ok(chunk) = self.iter.next_chunk_back() {
acc = f(acc, chunk)?
}
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index a00a6c4783ba6..e24b220acb43b 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -35,6 +35,7 @@ use crate::clone::TrivialClone;
use crate::cmp::Ordering;
use crate::marker::{Destruct, DiscriminantKind};
use crate::panic::const_assert;
+use crate::ub_checks::assert_unsafe_precondition;
use crate::{clone, cmp, fmt, hash, intrinsics, ptr};
mod alignment;
@@ -1077,6 +1078,27 @@ pub const fn copy(x: &T) -> T {
///
/// [ub]: ../../reference/behavior-considered-undefined.html
///
+/// If you have a raw pointer instead of a reference, you might be looking for
+/// `src.cast::().`[`read_unaligned()`](pointer#method.read_unaligned) instead.
+///
+/// # Safety
+///
+/// - Requires `size_of_val::(src) >= size_of::()`
+/// - The first `size_of::()` bytes behind `src` must be *readable*
+/// - The first `size_of::()` bytes behind `src` must be *[valid]*
+/// when interpreted as a `Dst`.
+///
+/// On top of that, remember that most types have additional invariants beyond merely
+/// being considered initialized at the type level. For example, a `1`-initialized [`Vec`]
+/// is considered initialized (under the current implementation; this does not constitute
+/// a stable guarantee) because the only requirement the compiler knows about it
+/// is that the data pointer must be non-null. Creating such a `Vec` does not cause
+/// *immediate* undefined behavior, but will cause undefined behavior with most
+/// safe operations (including dropping it).
+///
+/// [valid]: ../../reference/behavior-considered-undefined.html#r-undefined.validity
+/// [`Vec`]: ../../std/vec/struct.Vec.html
+///
/// # Examples
///
/// ```
@@ -1101,20 +1123,32 @@ pub const fn copy(x: &T) -> T {
///
/// // The contents of 'foo_array' should not have changed
/// assert_eq!(foo_array, [10]);
+///
+/// let bytes: &[u8] = &[1, 2, 3, 4, 5, 6, 7];
+/// assert_eq!(
+/// unsafe { mem::transmute_copy::<[u8], u32>(bytes) },
+/// u32::from_ne_bytes(*bytes.first_chunk().unwrap()),
+/// );
/// ```
#[inline]
#[must_use]
#[track_caller]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_transmute_copy", since = "1.74.0")]
-pub const unsafe fn transmute_copy(src: &Src) -> Dst {
- assert!(
- size_of::() >= size_of::(),
- "cannot transmute_copy if Dst is larger than Src"
+pub const unsafe fn transmute_copy(src: &Src) -> Dst {
+ // library UB because it's possible for the `Src` to be only a subset of the allocation
+ // and thus for a failure to not be immediate language UB
+ assert_unsafe_precondition!(
+ check_library_ub,
+ "cannot transmute_copy if Dst is larger than Src",
+ (
+ src_size: usize = size_of_val::(src),
+ dst_size: usize = Dst::SIZE,
+ ) => src_size >= dst_size
);
// If Dst has a higher alignment requirement, src might not be suitably aligned.
- if align_of::() > align_of::() {
+ if align_of::() > align_of_val::(src) {
// SAFETY: `src` is a reference which is guaranteed to be valid for reads.
// The caller must guarantee that the actual transmutation is safe.
unsafe { ptr::read_unaligned(src as *const Src as *const Dst) }
diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs
index b95e6e13063f5..793c9f489e47f 100644
--- a/library/coretests/tests/mem.rs
+++ b/library/coretests/tests/mem.rs
@@ -138,32 +138,6 @@ fn test_transmute_copy_unaligned() {
assert_eq!(0_u64, unsafe { transmute_copy(&u.b) });
}
-#[test]
-#[cfg(panic = "unwind")]
-fn test_transmute_copy_grow_panics() {
- use std::panic;
-
- let err = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
- let _unused: u64 = transmute_copy(&1_u8);
- }));
-
- match err {
- Ok(_) => unreachable!(),
- Err(payload) => {
- payload
- .downcast::<&'static str>()
- .and_then(|s| {
- if *s == "cannot transmute_copy if Dst is larger than Src" {
- Ok(s)
- } else {
- Err(s)
- }
- })
- .unwrap_or_else(|p| panic::resume_unwind(p));
- }
- }
-}
-
#[test]
#[allow(dead_code)]
fn test_discriminant_send_sync() {
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 750ddb91c482b..77be07b097b03 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -1540,6 +1540,8 @@ impl Seek for File {
(&*self).stream_position()
}
}
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
impl crate::io::IoHandle for File {}
impl Dir {
diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs
index 4d9eecb0d9648..075ea44377c78 100644
--- a/library/std/src/io/buffered/bufreader.rs
+++ b/library/std/src/io/buffered/bufreader.rs
@@ -580,6 +580,8 @@ impl Seek for BufReader {
}
}
+#[doc(hidden)]
+#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
impl SizeHint for BufReader {
#[inline]
fn lower_bound(&self) -> usize {
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index dc623a3e82c2c..b06ec9af7239d 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -299,7 +299,7 @@ mod tests;
use core::slice::memchr;
-use alloc_crate::io::OsFunctions;
+pub(crate) use alloc_crate::io::IoHandle;
#[unstable(feature = "raw_os_error_ty", issue = "107792")]
pub use alloc_crate::io::RawOsError;
#[doc(hidden)]
@@ -315,6 +315,7 @@ pub use alloc_crate::io::{
};
#[stable(feature = "iovec", since = "1.36.0")]
pub use alloc_crate::io::{IoSlice, IoSliceMut};
+use alloc_crate::io::{OsFunctions, SizeHint};
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
pub use self::buffered::WriterPanicked;
@@ -1980,21 +1981,6 @@ pub enum SeekFrom {
Current(#[stable(feature = "rust1", since = "1.0.0")] i64),
}
-/// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically
-/// implemented for handle types like [`Arc`][arc] as well.
-///
-/// This trait should only be implemented for types where `<&T as Trait>::method(&mut &value, ..)`
-/// would be identical to `::method(&mut value, ..)`.
-///
-/// [`File`][file] passes this test, as operations on `&File` and `File` both affect
-/// the same underlying file.
-/// `[u8]` fails, because any modification to `&mut &[u8]` would only affect a temporary
-/// and be lost after the method has been called.
-///
-/// [file]: crate::fs::File
-/// [arc]: crate::sync::Arc
-pub(crate) trait IoHandle {}
-
fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result {
let mut read = 0;
loop {
@@ -2552,21 +2538,6 @@ impl BufRead for Chain {
// split between the two parts of the chain
}
-impl SizeHint for Chain {
- #[inline]
- fn lower_bound(&self) -> usize {
- SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second)
- }
-
- #[inline]
- fn upper_bound(&self) -> Option {
- match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) {
- (Some(first), Some(second)) => first.checked_add(second),
- _ => None,
- }
- }
-}
-
#[stable(feature = "rust1", since = "1.0.0")]
impl Read for Take {
fn read(&mut self, buf: &mut [u8]) -> Result {
@@ -2661,21 +2632,6 @@ impl BufRead for Take {
}
}
-impl SizeHint for Take {
- #[inline]
- fn lower_bound(&self) -> usize {
- cmp::min(SizeHint::lower_bound(&self.inner) as u64, self.limit) as usize
- }
-
- #[inline]
- fn upper_bound(&self) -> Option {
- match SizeHint::upper_bound(&self.inner) {
- Some(upper_bound) => Some(cmp::min(upper_bound as u64, self.limit) as usize),
- None => self.limit.try_into().ok(),
- }
- }
-}
-
#[stable(feature = "seek_io_take", since = "1.89.0")]
impl Seek for Take {
fn seek(&mut self, pos: SeekFrom) -> Result {
@@ -2784,64 +2740,6 @@ fn uninlined_slow_read_byte(reader: &mut R) -> Option> {
inlined_slow_read_byte(reader)
}
-trait SizeHint {
- fn lower_bound(&self) -> usize;
-
- fn upper_bound(&self) -> Option;
-
- fn size_hint(&self) -> (usize, Option) {
- (self.lower_bound(), self.upper_bound())
- }
-}
-
-impl SizeHint for T {
- #[inline]
- default fn lower_bound(&self) -> usize {
- 0
- }
-
- #[inline]
- default fn upper_bound(&self) -> Option {
- None
- }
-}
-
-impl SizeHint for &mut T {
- #[inline]
- fn lower_bound(&self) -> usize {
- SizeHint::lower_bound(*self)
- }
-
- #[inline]
- fn upper_bound(&self) -> Option {
- SizeHint::upper_bound(*self)
- }
-}
-
-impl SizeHint for Box {
- #[inline]
- fn lower_bound(&self) -> usize {
- SizeHint::lower_bound(&**self)
- }
-
- #[inline]
- fn upper_bound(&self) -> Option {
- SizeHint::upper_bound(&**self)
- }
-}
-
-impl SizeHint for &[u8] {
- #[inline]
- fn lower_bound(&self) -> usize {
- self.len()
- }
-
- #[inline]
- fn upper_bound(&self) -> Option {
- Some(self.len())
- }
-}
-
/// An iterator over the contents of an instance of `BufRead` split on a
/// particular byte.
///
diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs
index dbda72a43a637..fbb69a11c6b92 100644
--- a/library/std/src/io/util.rs
+++ b/library/std/src/io/util.rs
@@ -6,7 +6,7 @@ mod tests;
use crate::fmt;
use crate::io::{
self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Seek, SeekFrom, Sink,
- SizeHint, Write,
+ Write,
};
#[stable(feature = "rust1", since = "1.0.0")]
@@ -102,13 +102,6 @@ impl Seek for Empty {
}
}
-impl SizeHint for Empty {
- #[inline]
- fn upper_bound(&self) -> Option {
- Some(0)
- }
-}
-
#[stable(feature = "empty_write", since = "1.73.0")]
impl Write for Empty {
#[inline]
@@ -240,18 +233,6 @@ impl Read for Repeat {
}
}
-impl SizeHint for Repeat {
- #[inline]
- fn lower_bound(&self) -> usize {
- usize::MAX
- }
-
- #[inline]
- fn upper_bound(&self) -> Option {
- None
- }
-}
-
#[stable(feature = "rust1", since = "1.0.0")]
impl Write for Sink {
#[inline]
diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs
index 791af5f8ee2a9..e4280520bd8ba 100644
--- a/library/test/src/lib.rs
+++ b/library/test/src/lib.rs
@@ -207,6 +207,14 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) {
// If we're being run in SpawnedSecondary mode, run the test here. run_test
// will then exit the process.
if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) {
+ // SAFETY: Technically, this is a racy access that we probably shouldn't do?
+ // In practice, this is completely fine as long as the test harness is made of Rust,
+ // as std will synchronize the racy accesses that occur when they happen through std::env.
+ // Any unsoundness can only be exposed in practice if e.g. C code also takes an interest
+ // in these variables.
+ //
+ // If we ever grow an actual story for libtest and start documenting custom harness reqs,
+ // we should either fix this being racy or say "write it in Rust, please".
unsafe {
env::remove_var(SECONDARY_TEST_INVOKER_VAR);
}
@@ -214,6 +222,7 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) {
// Convert benchmarks to tests if we're not benchmarking.
let mut tests = tests.iter().map(make_owned_test).collect::>();
if env::var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR).is_ok() {
+ // SAFETY: Same as for SECONDARY_TEST_INVOKER_VAR
unsafe {
env::remove_var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR);
}
diff --git a/src/ci/docker/host-x86_64/tidy/Dockerfile b/src/ci/docker/host-x86_64/tidy/Dockerfile
index 0c6029a6a5442..5ea9941e863c8 100644
--- a/src/ci/docker/host-x86_64/tidy/Dockerfile
+++ b/src/ci/docker/host-x86_64/tidy/Dockerfile
@@ -41,5 +41,4 @@ COPY host-x86_64/pr-check-1/validate-toolstate.sh /scripts/
# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
-ENV SCRIPT="TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
- src/tools/tidy tidyselftest --extra-checks=py,cpp,js,spellcheck"
+ENV SCRIPT="python2.7 ../x.py test src/tools/tidy tidyselftest --extra-checks=py,cpp,js,spellcheck"
diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh
index 54ede153f6153..eae2aec917b1c 100755
--- a/src/ci/docker/scripts/rfl-build.sh
+++ b/src/ci/docker/scripts/rfl-build.sh
@@ -2,7 +2,8 @@
set -euo pipefail
-LINUX_VERSION=v7.1-rc1
+# https://github.com/rust-lang/rust/pull/157151
+LINUX_VERSION=40bc55834bc15896f4438b0936c679ef32e20df1
# Build rustc, rustdoc, cargo, clippy-driver and rustfmt
../x.py build --stage 2 library rustdoc clippy rustfmt
diff --git a/src/doc/unstable-book/src/compiler-flags/force-intrinsic-fallback.md b/src/doc/unstable-book/src/compiler-flags/force-intrinsic-fallback.md
new file mode 100644
index 0000000000000..41f6ae3237908
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/force-intrinsic-fallback.md
@@ -0,0 +1,6 @@
+## `force-intrinsic-fallback`
+
+Configures codegen to always use the fallback body of an intrinsic, if it has one,
+instead of lowering the intrinsic in the codegen backend.
+
+This is useful for testing the fallback implementation.
diff --git a/src/etc/pre-push.sh b/src/etc/pre-push.sh
index 210edbf0cda66..fb2007db9f12c 100755
--- a/src/etc/pre-push.sh
+++ b/src/etc/pre-push.sh
@@ -33,8 +33,7 @@ ROOT_DIR="$(git rev-parse --show-toplevel)"
echo "Running pre-push script $ROOT_DIR/x test tidy"
cd "$ROOT_DIR"
-# The env var is necessary for printing diffs in py (fmt/lint) and cpp.
-TIDY_PRINT_DIFF=1 ./x test tidy \
+./x test tidy \
--set build.locked-deps=true \
--extra-checks auto:py,auto:cpp,auto:js
if [ $? -ne 0 ]; then
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 9469c806a26ad..d4cdc7781fed5 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -681,21 +681,26 @@ fn build_module(
// We are only interested into `Res::Def`. And in there, we only want "items" which get their own
// rustdoc page. So not `DefKind::Ctor` for example (which is returned by `tcx.module_children()`).
fn should_ignore_res(res: Res) -> bool {
+ !matches!(res, Res::Def(def_kind, _) if !should_ignore_def_kind(def_kind))
+}
+
+fn should_ignore_def_kind(kind: DefKind) -> bool {
!matches!(
- res,
- Res::Def(DefKind::Trait, _)
- | Res::Def(DefKind::TraitAlias, _)
- | Res::Def(DefKind::Fn, _)
- | Res::Def(DefKind::Struct, _)
- | Res::Def(DefKind::Union, _)
- | Res::Def(DefKind::TyAlias, _)
- | Res::Def(DefKind::Enum, _)
- | Res::Def(DefKind::ForeignTy, _)
- | Res::Def(DefKind::Variant, _)
- | Res::Def(DefKind::Mod, _)
- | Res::Def(DefKind::Static { .. }, _)
- | Res::Def(DefKind::Const { .. }, _)
- | Res::Def(DefKind::Macro(_), _)
+ kind,
+ DefKind::Trait
+ | DefKind::TraitAlias
+ | DefKind::Fn
+ | DefKind::Struct
+ | DefKind::Union
+ | DefKind::TyAlias
+ | DefKind::Enum
+ | DefKind::ForeignTy
+ | DefKind::Variant
+ | DefKind::Mod
+ | DefKind::Static { .. }
+ | DefKind::Const { .. }
+ | DefKind::Macro(_)
+ | DefKind::Use
)
}
@@ -770,13 +775,15 @@ fn build_module_items(
} else if let Some(def_id) = res.opt_def_id()
&& let Some(reexport) = item.reexport_chain.first()
&& let Some(reexport_def_id) = reexport.id()
+ && !should_ignore_def_kind(cx.tcx.def_kind(reexport_def_id))
&& find_attr!(
load_attrs(cx.tcx, reexport_def_id),
Doc(d)
if d.inline.first().is_some_and(|(inline, _)| *inline == hir::attrs::DocInline::NoInline)
)
{
- if should_ignore_res(res) {
+ // We don't inline foreign `use`.
+ if should_ignore_res(res) || matches!(res, Res::Def(DefKind::Use, _)) {
continue;
}
// This item is reexported as `no_inline` so it shouldn't be inlined.
diff --git a/src/tools/miri/tests/x86_64-unknown-kernel.json b/src/tools/miri/tests/x86_64-unknown-kernel.json
index 0f8032c39d568..c5e519429d71a 100644
--- a/src/tools/miri/tests/x86_64-unknown-kernel.json
+++ b/src/tools/miri/tests/x86_64-unknown-kernel.json
@@ -10,7 +10,7 @@
"vendor": "unknown",
"linker": "rust-lld",
"linker-flavor": "gnu-lld",
- "rustc-abi": "x86-softfloat",
+ "rustc-abi": "softfloat",
"features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float",
"dynamic-linking": false,
"executables": true,
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 89aae46ec8f23..9ad70538d1956 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -692,6 +692,9 @@ pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) {
/// Ensure the list of proc-macro crate transitive dependencies is up to date
fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut RunningCheck) {
+ if std::env::var("RUSTC").is_err() {
+ panic!("tidy must be run under bootstrap (./x test tidy), not as a standalone command");
+ }
let mut cmd = cargo_metadata::MetadataCommand::new();
cmd.cargo_path(cargo)
.manifest_path(root.join("Cargo.toml"))
diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs
index 008cc0322534a..d358858b24598 100644
--- a/src/tools/tidy/src/extra_checks/mod.rs
+++ b/src/tools/tidy/src/extra_checks/mod.rs
@@ -10,12 +10,11 @@
//! configuration after a double dash (`--extra-checks=py -- foo.py`)
//! 2. Build configuration based on args/environment:
//! - Formatters by default are in check only mode
-//! - If in CI (TIDY_PRINT_DIFF=1 is set), check and print the diff
//! - If `--bless` is provided, formatters may run
//! - Pass any additional config after the `--`. If no files are specified,
//! use a default.
-//! 3. Print the output of the given command. If it fails and `TIDY_PRINT_DIFF`
-//! is set, rerun the tool to print a suggestion diff (for e.g. CI)
+//! 3. Print the output of the given command. If it fails, rerun the tool to print a suggestion
+//! diff.
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
@@ -200,14 +199,12 @@ fn js_prepare(root_path: &Path, outdir: &Path, npm: &Path, tidy_ctx: &TidyCtx) -
fn show_bless_help(mode: &str, action: &str, bless: bool) {
if !bless {
- eprintln!("rerun tidy with `--extra-checks={mode} --bless` to {action}");
+ eprintln!(
+ "rerun with `--bless` to {action}: `./x.py test tidy --extra-checks={mode} --bless`"
+ );
}
}
-fn show_diff() -> bool {
- std::env::var("TIDY_PRINT_DIFF").is_ok_and(|v| v.eq_ignore_ascii_case("true") || v == "1")
-}
-
fn check_spellcheck(root_path: &Path, outdir: &Path, cargo: &Path, tidy_ctx: &TidyCtx) {
let mut check = tidy_ctx.start_check("extra_checks:spellcheck");
@@ -314,7 +311,7 @@ fn check_python_lint(
let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, args);
- if res.is_err() && !bless && show_diff() {
+ if res.is_err() && !bless {
eprintln!("\npython linting failed! Printing diff suggestions:");
let diff_res = run_ruff(
@@ -358,18 +355,16 @@ fn check_python_fmt(
let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &args);
if res.is_err() && !bless {
- if show_diff() {
- eprintln!("\npython formatting does not match! Printing diff:");
-
- let _ = run_ruff(
- root_path,
- outdir,
- py_path,
- &cfg_args,
- &file_args,
- &["format".as_ref(), "--diff".as_ref()],
- );
- }
+ eprintln!("\npython formatting does not match! Printing diff:");
+
+ let _ = run_ruff(
+ root_path,
+ outdir,
+ py_path,
+ &cfg_args,
+ &file_args,
+ &["format".as_ref(), "--diff".as_ref()],
+ );
show_bless_help("py:fmt", "reformat Python code", bless);
}
@@ -421,7 +416,7 @@ fn check_cpp_fmt(
let args = merge_args(&cfg_args_clang_format, &file_args_clang_format);
let res = py_runner(py_path, false, None, "clang-format", &args);
- if res.is_err() && !bless && show_diff() {
+ if res.is_err() && !bless {
eprintln!("\nclang-format linting failed! Printing diff suggestions:");
let mut cfg_args_diff = cfg_args.clone();
diff --git a/tests/codegen-llvm/autodiff/abi_handling.rs b/tests/codegen-llvm/autodiff/abi_handling.rs
index a8bc482fc293f..90a36823060cd 100644
--- a/tests/codegen-llvm/autodiff/abi_handling.rs
+++ b/tests/codegen-llvm/autodiff/abi_handling.rs
@@ -1,7 +1,7 @@
//@ revisions: debug release
//@[debug] compile-flags: -Zautodiff=Enable,NoTT -C opt-level=0 -Clto=fat
-//@[release] compile-flags: -Zautodiff=Enable,NoTT -C opt-level=3 -Clto=fat
+//@[release] compile-flags: -Zautodiff=Enable,NoTT -Zautodiff_post_passes=function(mem2reg,instsimplify,simplifycfg) -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
@@ -39,15 +39,15 @@ fn square(x: f32) -> f32 {
// CHECK-NEXT: Function Attrs
// debug-NEXT: define internal { float, float }
// debug-SAME: (ptr {{.*}}%x, ptr {{.*}}%bx_0)
-// release-NEXT: define internal fastcc float
-// release-SAME: (float %x.0.val, float %x.4.val)
+// release-NEXT: define internal fastcc { float, float }
+// release-SAME: (ptr {{.*}}%x)
// CHECK-LABEL: ; abi_handling::f1
// CHECK-NEXT: Function Attrs
// debug-NEXT: define internal float
// debug-SAME: (ptr {{.*}}%x)
-// release-NEXT: define internal fastcc noundef float
-// release-SAME: (float %x.0.val, float %x.4.val)
+// release-NEXT: define internal noundef float
+// release-SAME: (ptr {{.*}}%x)
#[autodiff_forward(df1, Dual, Dual)]
#[inline(never)]
fn f1(x: &[f32; 2]) -> f32 {
@@ -58,15 +58,15 @@ fn f1(x: &[f32; 2]) -> f32 {
// CHECK-NEXT: Function Attrs
// debug-NEXT: define internal { float, float }
// debug-SAME: (ptr %f, float %x, float %dret)
-// release-NEXT: define internal fastcc noundef float
+// release-NEXT: define internal fastcc { float, float }
// release-SAME: (float noundef %x)
// CHECK-LABEL: ; abi_handling::f2
// CHECK-NEXT: Function Attrs
// debug-NEXT: define internal float
// debug-SAME: (ptr %f, float %x)
-// release-NEXT: define internal fastcc noundef float
-// release-SAME: (float noundef %x)
+// release-NEXT: define internal noundef float
+// release-SAME: (ptr {{.*}}%f, float {{.*}}%x)
#[autodiff_reverse(df2, Const, Active, Active)]
#[inline(never)]
fn f2(f: fn(f32) -> f32, x: f32) -> f32 {
@@ -77,15 +77,15 @@ fn f2(f: fn(f32) -> f32, x: f32) -> f32 {
// CHECK-NEXT: Function Attrs
// debug-NEXT: define internal { float, float }
// debug-SAME: (ptr align 4 %x, ptr align 4 %bx_0, ptr align 4 %y, ptr align 4 %by_0)
-// release-NEXT: define internal fastcc float
-// release-SAME: (float %x.0.val)
+// release-NEXT: define internal fastcc { float, float }
+// release-SAME: (ptr {{.*}}%x)
// CHECK-LABEL: ; abi_handling::f3
// CHECK-NEXT: Function Attrs
// debug-NEXT: define internal float
// debug-SAME: (ptr {{.*}}%x, ptr {{.*}}%y)
-// release-NEXT: define internal fastcc noundef float
-// release-SAME: (float %x.0.val)
+// release-NEXT: define internal noundef float
+// release-SAME: (ptr {{.*}}%y)
#[autodiff_forward(df3, Dual, Dual, Dual)]
#[inline(never)]
fn f3<'a>(x: &'a f32, y: &'a f32) -> f32 {
@@ -103,7 +103,7 @@ fn f3<'a>(x: &'a f32, y: &'a f32) -> f32 {
// CHECK-NEXT: Function Attrs
// debug-NEXT: define internal float
// debug-SAME: (float %x.0, float %x.1)
-// release-NEXT: define internal fastcc noundef float
+// release-NEXT: define internal noundef float
// release-SAME: (float noundef %x.0, float noundef %x.1)
#[autodiff_forward(df4, Dual, Dual)]
#[inline(never)]
@@ -122,7 +122,7 @@ fn f4(x: (f32, f32)) -> f32 {
// CHECK-NEXT: Function Attrs
// debug-NEXT: define internal float
// debug-SAME: (float %i.0, float %i.1)
-// release-NEXT: define internal fastcc noundef float
+// release-NEXT: define internal noundef float
// release-SAME: (float noundef %i.0, float noundef %i.1)
#[autodiff_forward(df5, Dual, Dual)]
#[inline(never)]
@@ -142,7 +142,7 @@ fn f5(i: Input) -> f32 {
// CHECK-NEXT: Function Attrs
// debug-NEXT: define internal float
// debug-SAME: (float %i.0, float %i.1)
-// release-NEXT: define internal fastcc noundef float
+// release-NEXT: define internal noundef float
// release-SAME: (float noundef %i.0, float noundef %i.1)
#[autodiff_forward(df6, Dual, Dual)]
#[inline(never)]
@@ -155,14 +155,14 @@ fn f6(i: NestedInput) -> f32 {
// debug-NEXT: define internal { float, float }
// debug-SAME: (ptr align 4 %x.0, ptr align 4 %x.1, ptr align 4 %bx_0.0, ptr align 4 %bx_0.1)
// release-NEXT: define internal fastcc { float, float }
-// release-SAME: (float %x.0.0.val, float %x.1.0.val)
+// release-SAME: (ptr {{.*}}%x.0, ptr {{.*}}%x.1)
// CHECK-LABEL: ; abi_handling::f7
// CHECK-NEXT: Function Attrs
// debug-NEXT: define internal float
// debug-SAME: (ptr {{.*}}%x.0, ptr {{.*}}%x.1)
-// release-NEXT: define internal fastcc noundef float
-// release-SAME: (float %x.0.0.val, float %x.1.0.val)
+// release-NEXT: define internal noundef float
+// release-SAME: (ptr {{.*}}%x.0, ptr {{.*}}%x.1)
#[autodiff_forward(df7, Dual, Dual)]
#[inline(never)]
fn f7(x: (&f32, &f32)) -> f32 {
diff --git a/tests/codegen-llvm/autodiff/autodiffv2.rs b/tests/codegen-llvm/autodiff/autodiffv2.rs
index c24a374148c34..10e619f84335e 100644
--- a/tests/codegen-llvm/autodiff/autodiffv2.rs
+++ b/tests/codegen-llvm/autodiff/autodiffv2.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat
+//@ compile-flags: -Zautodiff=Enable,NoTT -Zautodiff_post_passes=function(mem2reg,instsimplify,simplifycfg) -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
//
diff --git a/tests/codegen-llvm/autodiff/batched.rs b/tests/codegen-llvm/autodiff/batched.rs
index 5a723ff041839..64d14a0f9c80e 100644
--- a/tests/codegen-llvm/autodiff/batched.rs
+++ b/tests/codegen-llvm/autodiff/batched.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Zautodiff=Enable,NoTT,NoPostopt -C opt-level=3 -Clto=fat
+//@ compile-flags: -Zautodiff=Enable,NoTT -Zautodiff_post_passes=function(mem2reg,instsimplify,simplifycfg) -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
@@ -20,22 +20,28 @@ fn square(x: &f32) -> f32 {
x * x
}
-// The base ("scalar") case d_square3, without batching.
-// CHECK: define internal fastcc float @fwddiffesquare(float %x.0.val, float %"x'.0.val")
-// CHECK: %0 = fadd fast float %"x'.0.val", %"x'.0.val"
-// CHECK-NEXT: %1 = fmul fast float %0, %x.0.val
-// CHECK-NEXT: ret float %1
-// CHECK-NEXT: }
-
-// d_square2
-// CHECK: define internal fastcc [4 x float] @fwddiffe4square(float %x.0.val, [4 x ptr] %"x'")
-// CHECK: ret [4 x float]
+// CHECK: ; batched::d_square2
+// CHECK: define internal fastcc void
+// CHECK-SAME: (ptr {{.*}}, ptr {{.*}}, ptr {{.*}}, ptr {{.*}}, ptr {{.*}})
+// CHECK: ret void
// CHECK-NEXT: }
-// CHECK: define internal fastcc { float, [4 x float] } @fwddiffe4square.{{.*}}(float %x.0.val, [4 x ptr] %"x'")
-// CHECK: ret { float, [4 x float] }
+// CHECK: ; batched::d_square1
+// CHECK: define internal fastcc void
+// CHECK-SAME: (ptr {{.*}}, ptr {{.*}}, ptr {{.*}}, ptr {{.*}}, ptr {{.*}}, ptr {{.*}})
+// CHECK: ret void
// CHECK-NEXT: }
+// The base ("scalar") case d_square3, without batching.
+// CHECK: define internal float @fwddiffesquare(ptr {{.*}}, ptr {{.*}})
+// CHECK: [[SHADOW_X:%"_[0-9]+'ipl"]] = load float, ptr %"x'"
+// CHECK-NEXT: [[PRIMAL_X:%_[0-9]+]] = load float, ptr %x
+// CHECK-NEXT: [[MUL1:%[0-9]+]] = fmul fast float [[SHADOW_X]], [[PRIMAL_X]]
+// CHECK-NEXT: [[MUL2:%[0-9]+]] = fmul fast float [[SHADOW_X]], [[PRIMAL_X]]
+// CHECK-NEXT: [[ADD1:%[0-9]+]] = fadd fast float [[MUL1]], [[MUL2]]
+// CHECK-NEXT: ret float [[ADD1]]
+// CHECK-NEXT: }
+
fn main() {
let x = std::hint::black_box(3.0);
let output = square(&x);
diff --git a/tests/codegen-llvm/autodiff/generic.rs b/tests/codegen-llvm/autodiff/generic.rs
index b31468c91c9c2..1c7ce7c544db5 100644
--- a/tests/codegen-llvm/autodiff/generic.rs
+++ b/tests/codegen-llvm/autodiff/generic.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Zautodiff=Enable -Zautodiff=NoPostopt -C opt-level=3 -Clto=fat
+//@ compile-flags: -Zautodiff=Enable -Zautodiff_post_passes=function(mem2reg,instsimplify,simplifycfg) -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
//@ revisions: F32 F64 Main
@@ -34,7 +34,7 @@ fn square + Copy>(x: &T) -> T {
// F64-NEXT: ; Function Attrs: {{.*}}
// F64-NEXT: define internal {{.*}} void
// F64-NEXT: start:
-// F64-NEXT: {{(tail )?}}call {{(fastcc )?}}void @diffe_{{.*}}(double {{.*}}, ptr {{.*}})
+// F64-NEXT: {{(tail )?}}call fast { double } @diffe_{{.*}}(ptr {{.*}}, ptr {{.*}}, double {{.*}})
// F64-NEXT: ret void
// Main-LABEL: ; generic::main
diff --git a/tests/codegen-llvm/autodiff/identical_fnc.rs b/tests/codegen-llvm/autodiff/identical_fnc.rs
index a8b186c302ea1..294ffaa8f719b 100644
--- a/tests/codegen-llvm/autodiff/identical_fnc.rs
+++ b/tests/codegen-llvm/autodiff/identical_fnc.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat
+//@ compile-flags: -Zautodiff=Enable -Zautodiff_post_passes=mergefunc,function(mem2reg,instsimplify,simplifycfg) -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
//
@@ -32,9 +32,9 @@ fn square2(x: &f64) -> f64 {
// CHECK-NOT:br
// CHECK-NOT:ret
// CHECK:; call identical_fnc::d_square
-// CHECK-NEXT:call fastcc void @[[HASH:.+]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1)
+// CHECK-NEXT:call fastcc void @[[HASH:.+]](ptr {{.*}}, ptr {{.*}})
// CHECK:; call identical_fnc::d_square
-// CHECK-NEXT:call fastcc void @[[HASH]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2)
+// CHECK-NEXT:call fastcc void @[[HASH]](ptr {{.*}}, ptr {{.*}})
fn main() {
let x = std::hint::black_box(3.0);
diff --git a/tests/codegen-llvm/autodiff/impl.rs b/tests/codegen-llvm/autodiff/impl.rs
index 185ea6af52e0f..ffcf28e472e8b 100644
--- a/tests/codegen-llvm/autodiff/impl.rs
+++ b/tests/codegen-llvm/autodiff/impl.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Zautodiff=Enable -Zautodiff=NoPostopt -C opt-level=3 -Clto=fat
+//@ compile-flags: -Zautodiff=Enable -Zautodiff_post_passes=function(mem2reg,instsimplify,simplifycfg) -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
diff --git a/tests/codegen-llvm/autodiff/scalar.rs b/tests/codegen-llvm/autodiff/scalar.rs
index 53672a89230ab..74e0bc6e32099 100644
--- a/tests/codegen-llvm/autodiff/scalar.rs
+++ b/tests/codegen-llvm/autodiff/scalar.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Zautodiff=Enable,NoTT -C opt-level=3 -Clto=fat
+//@ compile-flags: -Zautodiff=Enable,NoTT -Zautodiff_post_passes=function(mem2reg,instsimplify,simplifycfg) -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
#![feature(autodiff)]
@@ -12,14 +12,18 @@ fn square(x: &f64) -> f64 {
x * x
}
-// CHECK:define internal fastcc double @diffesquare(double %x.0.val, ptr nonnull align 8 captures(none) %"x'")
-// CHECK-NEXT:invertstart:
-// CHECK-NEXT: %_0 = fmul double %x.0.val, %x.0.val
-// CHECK-NEXT: %0 = fadd fast double %x.0.val, %x.0.val
-// CHECK-NEXT: %1 = load double, ptr %"x'", align 8
-// CHECK-NEXT: %2 = fadd fast double %1, %0
-// CHECK-NEXT: store double %2, ptr %"x'", align 8
-// CHECK-NEXT: ret double %_0
+// CHECK:define internal { double } @diffesquare(ptr {{.*}}, ptr {{.*}}, double {{.*}})
+// CHECK-NEXT:start:
+// CHECK-NEXT: [[X:%_[0-9]+]] = load double, ptr %x, align 8
+// CHECK-NEXT: [[SQUARE:%_[0-9]+]] = fmul double [[X]], [[X]]
+// CHECK-NEXT: [[DIFFR1:%[0-9]+]] = fmul fast double %differeturn, [[X]]
+// CHECK-NEXT: [[DIFFR2:%[0-9]+]] = fmul fast double %differeturn, [[X]]
+// CHECK-NEXT: [[ADD1:%[0-9]+]] = fadd fast double [[DIFFR1]], [[DIFFR2]]
+// CHECK-NEXT: [[SHADOW_X:%[0-9]+]] = load double, ptr %"x'", align 8
+// CHECK-NEXT: [[ADD2:%[0-9]+]] = fadd fast double [[SHADOW_X]], [[ADD1]]
+// CHECK-NEXT: store double [[ADD2]], ptr %"x'", align 8
+// CHECK-NEXT: [[RET:%[0-9]+]] = insertvalue { double } undef, double [[SQUARE]], 0
+// CHECK-NEXT: ret { double } [[RET]]
// CHECK-NEXT:}
fn main() {
diff --git a/tests/codegen-llvm/autodiff/sret.rs b/tests/codegen-llvm/autodiff/sret.rs
index 498cd3fea012d..00ad0a58ffd08 100644
--- a/tests/codegen-llvm/autodiff/sret.rs
+++ b/tests/codegen-llvm/autodiff/sret.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Zautodiff=Enable,NoTT -C opt-level=3 -Clto=fat
+//@ compile-flags: -Zautodiff=Enable,NoTT -Zautodiff_post_passes=function(mem2reg,instsimplify,simplifycfg) -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
@@ -18,17 +18,21 @@ fn primal(x: f32, y: f32) -> f64 {
(x * x * y) as f64
}
-// CHECK: define internal fastcc { double, float, float } @diffeprimal(float noundef %x, float noundef %y)
-// CHECK-NEXT: invertstart:
+// CHECK: define internal { double, float, float } @diffeprimal(float noundef %x, float noundef %y, double %differeturn)
+// CHECK-NEXT: start:
// CHECK-NEXT: %_4 = fmul float %x, %x
// CHECK-NEXT: %_3 = fmul float %_4, %y
// CHECK-NEXT: %_0 = fpext float %_3 to double
-// CHECK-NEXT: %0 = fadd fast float %y, %y
-// CHECK-NEXT: %1 = fmul fast float %0, %x
-// CHECK-NEXT: %2 = insertvalue { double, float, float } undef, double %_0, 0
-// CHECK-NEXT: %3 = insertvalue { double, float, float } %2, float %1, 1
-// CHECK-NEXT: %4 = insertvalue { double, float, float } %3, float %_4, 2
-// CHECK-NEXT: ret { double, float, float } %4
+// CHECK-NEXT: %0 = fptrunc fast double %differeturn to float
+// CHECK-NEXT: %1 = fmul fast float %0, %y
+// CHECK-NEXT: %2 = fmul fast float %0, %_4
+// CHECK-NEXT: %3 = fmul fast float %1, %x
+// CHECK-NEXT: %4 = fmul fast float %1, %x
+// CHECK-NEXT: %5 = fadd fast float %3, %4
+// CHECK-NEXT: %6 = insertvalue { double, float, float } undef, double %_0, 0
+// CHECK-NEXT: %7 = insertvalue { double, float, float } %6, float %5, 1
+// CHECK-NEXT: %8 = insertvalue { double, float, float } %7, float %2, 2
+// CHECK-NEXT: ret { double, float, float } %8
// CHECK-NEXT: }
fn main() {
diff --git a/tests/codegen-llvm/autodiff/trait.rs b/tests/codegen-llvm/autodiff/trait.rs
index 701f3a9e843bd..f82ceac2512dc 100644
--- a/tests/codegen-llvm/autodiff/trait.rs
+++ b/tests/codegen-llvm/autodiff/trait.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Zautodiff=Enable -Zautodiff=NoPostopt -C opt-level=3 -Clto=fat
+//@ compile-flags: -Zautodiff=Enable -Zautodiff_post_passes=function(mem2reg,instsimplify,simplifycfg) -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
diff --git a/tests/codegen-llvm/autodiff/typetree.rs b/tests/codegen-llvm/autodiff/typetree.rs
index 1cb0c2fb68be3..4c270c3ad58ec 100644
--- a/tests/codegen-llvm/autodiff/typetree.rs
+++ b/tests/codegen-llvm/autodiff/typetree.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat
+//@ compile-flags: -Zautodiff=Enable -Zautodiff_post_passes=function(mem2reg,instsimplify,simplifycfg) -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
diff --git a/tests/codegen-llvm/autodiff/void_ret.rs b/tests/codegen-llvm/autodiff/void_ret.rs
index 98c6b98eef4e3..e80c93d23a5be 100644
--- a/tests/codegen-llvm/autodiff/void_ret.rs
+++ b/tests/codegen-llvm/autodiff/void_ret.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: -Zautodiff=Enable,NoTT,NoPostopt -C no-prepopulate-passes -C opt-level=3 -Clto=fat
+//@ compile-flags: -Zautodiff=Enable,NoTT -Zautodiff_post_passes=function(mem2reg,instsimplify,simplifycfg) -C no-prepopulate-passes -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
diff --git a/tests/codegen-llvm/force-intrinsic-fallback.rs b/tests/codegen-llvm/force-intrinsic-fallback.rs
new file mode 100644
index 0000000000000..4719745ed673a
--- /dev/null
+++ b/tests/codegen-llvm/force-intrinsic-fallback.rs
@@ -0,0 +1,38 @@
+//@ compile-flags: --crate-type=lib -C no-prepopulate-passes -Copt-level=3
+//
+//@ revisions: NORMAL FALLBACK
+//@ [FALLBACK] compile-flags: -Zforce-intrinsic-fallback
+#![feature(core_intrinsics, funnel_shifts)]
+
+// Check the effect of `-Zforce-intrinsic-fallback`.
+//
+// Without the flag, the dedicated backend lowering of the intrinsic is used.
+// With the flag, the fallback body is called instead.
+
+#[no_mangle]
+pub fn call_minimumf32(x: f32, y: f32) -> f32 {
+ // CHECK-LABEL: @call_minimumf32
+
+ // NORMAL: call float @llvm.minimum.f32
+ // NORMAL-NOT: minimumf32
+
+ // FALLBACK-NOT: @llvm.minimum
+ // FALLBACK: call noundef float @{{.*}}minimumf32
+ core::intrinsics::minimumf32(x, y)
+}
+
+// Codegen backends can return a list of `replaced_intrinsics`, for which codegen of the fallback is
+// normally skipped. `unchecked_funnel_shl` is in that list for the LLVM backend, so we test it here
+// to ensure that with the flag enabled the fallback body is actually code generated and called.
+
+#[no_mangle]
+pub fn call_funnel_shl(a: u32, b: u32, shift: u32) -> u32 {
+ // CHECK-LABEL: @call_funnel_shl
+
+ // NORMAL: call i32 @llvm.fshl.i32
+ // NORMAL-NOT: funnel_shl
+
+ // FALLBACK-NOT: @llvm.fshl
+ // FALLBACK: call noundef i32 @{{.*}}funnel_shl
+ unsafe { core::intrinsics::unchecked_funnel_shl(a, b, shift) }
+}
diff --git a/tests/codegen-llvm/lib-optimizations/transmute_copy.rs b/tests/codegen-llvm/lib-optimizations/transmute_copy.rs
new file mode 100644
index 0000000000000..bc96fd08c74aa
--- /dev/null
+++ b/tests/codegen-llvm/lib-optimizations/transmute_copy.rs
@@ -0,0 +1,65 @@
+//@ compile-flags: -Copt-level=1
+//@ only-64bit
+
+#![crate_type = "lib"]
+
+// Check that we don't have runtime alignment checks, just a safe alignment.
+// (Rust emits a branch, but LLVM merges the loads from the arms.)
+
+use std::mem::transmute_copy;
+
+// CHECK-LABEL: @transmute_copy_i16_from_i32_slice
+// CHECK-SAME: ptr{{.+}}%_0,
+// CHECK-SAME: ptr{{.+}}%x.0,
+// CHECK-SAME: i64{{.+}}%x.1)
+#[no_mangle]
+pub unsafe fn transmute_copy_i16_from_i32_slice(x: &[i32]) -> [i16; 7] {
+ // CHECK: start:
+ // CHECK-NEXT: @llvm.memcpy
+ // CHECK-SAME: align 2{{.+}}%_0,
+ // CHECK-SAME: align 4{{.+}}%x.0,
+ // CHECK-SAME: i64 14,
+ // CHECK-NEXT: ret void
+ transmute_copy(x)
+}
+
+// CHECK-LABEL: @transmute_copy_i32_from_i16_slice
+// CHECK-SAME: ptr{{.+}}%_0,
+// CHECK-SAME: ptr{{.+}}%x.0,
+// CHECK-SAME: i64{{.+}}%x.1)
+#[no_mangle]
+pub unsafe fn transmute_copy_i32_from_i16_slice(x: &[i16]) -> [i32; 3] {
+ // CHECK: start:
+ // CHECK-NEXT: @llvm.memcpy
+ // CHECK-SAME: align 4{{.+}}%_0,
+ // CHECK-SAME: align 2{{.+}}%x.0,
+ // CHECK-SAME: i64 12,
+ // CHECK-NEXT: ret void
+ transmute_copy(x)
+}
+
+// CHECK-LABEL: i32 @transmute_copy_i32_from_dyn
+// CHECK-SAME: ptr{{.+}}%x.0,
+// CHECK-SAME: ptr{{.+}}%x.1)
+#[no_mangle]
+pub unsafe fn transmute_copy_i32_from_dyn(x: &dyn std::fmt::Debug) -> i32 {
+ // CHECK: start:
+ // CHECK-NEXT: [[TEMP:%.+]] = load i32, ptr %x.0, align 1
+ // CHECK-NEXT: ret i32 [[TEMP]]
+ transmute_copy(x)
+}
+
+// CHECK-LABEL: @transmute_copy_i16_array_from_dyn
+// CHECK-SAME: ptr{{.+}}%_0,
+// CHECK-SAME: ptr{{.+}}%x.0,
+// CHECK-SAME: ptr{{.+}}%x.1)
+#[no_mangle]
+pub unsafe fn transmute_copy_i16_array_from_dyn(x: &dyn std::fmt::Debug) -> [i16; 7] {
+ // CHECK: start:
+ // CHECK-NEXT: @llvm.memcpy
+ // CHECK-SAME: align 2{{.+}}%_0,
+ // CHECK-SAME: align 1{{.+}}%x.0,
+ // CHECK-SAME: i64 14,
+ // CHECK-NEXT: ret void
+ transmute_copy(x)
+}
diff --git a/tests/rustdoc-html/reexport/auxiliary/glob-import.rs b/tests/rustdoc-html/reexport/auxiliary/glob-import.rs
new file mode 100644
index 0000000000000..2f8114586c592
--- /dev/null
+++ b/tests/rustdoc-html/reexport/auxiliary/glob-import.rs
@@ -0,0 +1 @@
+pub extern crate std as wgc;
diff --git a/tests/rustdoc-html/reexport/glob-import.rs b/tests/rustdoc-html/reexport/glob-import.rs
new file mode 100644
index 0000000000000..1bdfc7bdce102
--- /dev/null
+++ b/tests/rustdoc-html/reexport/glob-import.rs
@@ -0,0 +1,17 @@
+// This test ensures that inlining a foreign crate through a glob import doesn't
+// panic (because we're trying to retrieve attributes from an item which doesn't
+// have attributes).
+// Regression test for .
+
+//@ aux-build: glob-import.rs
+
+#![crate_name = "foo"]
+
+extern crate glob_import;
+
+//@ has 'foo/index.html'
+// There should be only one item listed: `wgc` (the only `glob-import` dep public item).
+//@ count - '//*[@id="main-content"]/h2[@class="section-header"]' 1
+//@ count - '//*[@id="main-content"]/dl[@class="item-table"]/dt' 1
+
+pub use glob_import::*;
diff --git a/tests/ui/iterators/into-iter-on-arrays-2018.rs b/tests/ui/iterators/into-iter-on-arrays-2018.rs
index d23544052362f..2999d085c8738 100644
--- a/tests/ui/iterators/into-iter-on-arrays-2018.rs
+++ b/tests/ui/iterators/into-iter-on-arrays-2018.rs
@@ -5,6 +5,7 @@ use std::array::IntoIter;
use std::ops::Deref;
use std::rc::Rc;
use std::slice::Iter;
+use std::boxed::BoxedArrayIntoIter;
fn main() {
let array = [0; 10];
@@ -15,9 +16,7 @@ fn main() {
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
- let _: Iter<'_, i32> = Box::new(array).into_iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
+ let _: BoxedArrayIntoIter = Box::new(array).into_iter();
let _: Iter<'_, i32> = Rc::new(array).into_iter();
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
diff --git a/tests/ui/iterators/into-iter-on-arrays-2018.stderr b/tests/ui/iterators/into-iter-on-arrays-2018.stderr
index 6419d779b4f57..769ea451a4f14 100644
--- a/tests/ui/iterators/into-iter-on-arrays-2018.stderr
+++ b/tests/ui/iterators/into-iter-on-arrays-2018.stderr
@@ -1,5 +1,5 @@
warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-2018.rs:14:34
+ --> $DIR/into-iter-on-arrays-2018.rs:15:34
|
LL | let _: Iter<'_, i32> = array.into_iter();
| ^^^^^^^^^
@@ -19,16 +19,7 @@ LL + let _: Iter<'_, i32> = IntoIterator::into_iter(array);
|
warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-2018.rs:18:44
- |
-LL | let _: Iter<'_, i32> = Box::new(array).into_iter();
- | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
- |
- = warning: this changes meaning in Rust 2021
- = note: for more information, see
-
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-2018.rs:22:43
+ --> $DIR/into-iter-on-arrays-2018.rs:21:43
|
LL | let _: Iter<'_, i32> = Rc::new(array).into_iter();
| ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
@@ -37,7 +28,7 @@ LL | let _: Iter<'_, i32> = Rc::new(array).into_iter();
= note: for more information, see
warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-2018.rs:25:41
+ --> $DIR/into-iter-on-arrays-2018.rs:24:41
|
LL | let _: Iter<'_, i32> = Array(array).into_iter();
| ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
@@ -46,7 +37,7 @@ LL | let _: Iter<'_, i32> = Array(array).into_iter();
= note: for more information, see
warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-2018.rs:32:24
+ --> $DIR/into-iter-on-arrays-2018.rs:31:24
|
LL | for _ in [1, 2, 3].into_iter() {}
| ^^^^^^^^^
@@ -64,5 +55,5 @@ LL - for _ in [1, 2, 3].into_iter() {}
LL + for _ in [1, 2, 3] {}
|
-warning: 5 warnings emitted
+warning: 4 warnings emitted
diff --git a/tests/ui/iterators/into-iter-on-arrays-2021.rs b/tests/ui/iterators/into-iter-on-arrays-2021.rs
index 1bda0ebf6cb8e..918cc4cf0801f 100644
--- a/tests/ui/iterators/into-iter-on-arrays-2021.rs
+++ b/tests/ui/iterators/into-iter-on-arrays-2021.rs
@@ -4,13 +4,14 @@
use std::array::IntoIter;
use std::ops::Deref;
use std::rc::Rc;
+use std::boxed::BoxedArrayIntoIter;
fn main() {
let array = [0; 10];
// In 2021, the method dispatches to `IntoIterator for [T; N]`.
let _: IntoIter = array.into_iter();
- let _: IntoIter = Box::new(array).into_iter();
+ let _: BoxedArrayIntoIter = Box::new(array).into_iter();
// The `array_into_iter` lint doesn't cover other wrappers that deref to an array.
let _: IntoIter = Rc::new(array).into_iter();
diff --git a/tests/ui/iterators/into-iter-on-arrays-lint.fixed b/tests/ui/iterators/into-iter-on-arrays-lint.fixed
index 848b13750d77a..0c105a55871b4 100644
--- a/tests/ui/iterators/into-iter-on-arrays-lint.fixed
+++ b/tests/ui/iterators/into-iter-on-arrays-lint.fixed
@@ -22,31 +22,15 @@ fn main() {
//~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
//~| WARNING this changes meaning
- Box::new(small).iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
- Box::new([1, 2]).iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
- Box::new(big).iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
- Box::new([0u8; 33]).iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
+ Box::new(small).into_iter();
+ Box::new([1, 2]).into_iter();
+ Box::new(big).into_iter();
+ Box::new([0u8; 33]).into_iter();
- Box::new(Box::new(small)).iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
- Box::new(Box::new([1, 2])).iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
- Box::new(Box::new(big)).iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
- Box::new(Box::new([0u8; 33])).iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
+ Box::new(Box::new(small)).into_iter();
+ Box::new(Box::new([1, 2])).into_iter();
+ Box::new(Box::new(big)).into_iter();
+ Box::new(Box::new([0u8; 33])).into_iter();
// Expressions that should not
(&[1, 2]).into_iter();
diff --git a/tests/ui/iterators/into-iter-on-arrays-lint.rs b/tests/ui/iterators/into-iter-on-arrays-lint.rs
index a505ba8313f56..6b9d840f138fd 100644
--- a/tests/ui/iterators/into-iter-on-arrays-lint.rs
+++ b/tests/ui/iterators/into-iter-on-arrays-lint.rs
@@ -23,30 +23,14 @@ fn main() {
//~| WARNING this changes meaning
Box::new(small).into_iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
Box::new([1, 2]).into_iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
Box::new(big).into_iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
Box::new([0u8; 33]).into_iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
Box::new(Box::new(small)).into_iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
Box::new(Box::new([1, 2])).into_iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
Box::new(Box::new(big)).into_iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
Box::new(Box::new([0u8; 33])).into_iter();
- //~^ WARNING this method call resolves to `<&[T; N] as IntoIterator>::into_iter`
- //~| WARNING this changes meaning
// Expressions that should not
(&[1, 2]).into_iter();
diff --git a/tests/ui/iterators/into-iter-on-arrays-lint.stderr b/tests/ui/iterators/into-iter-on-arrays-lint.stderr
index b173eb01d22ec..44375d20717b8 100644
--- a/tests/ui/iterators/into-iter-on-arrays-lint.stderr
+++ b/tests/ui/iterators/into-iter-on-arrays-lint.stderr
@@ -75,77 +75,5 @@ LL - [0u8; 33].into_iter();
LL + IntoIterator::into_iter([0u8; 33]);
|
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-lint.rs:25:21
- |
-LL | Box::new(small).into_iter();
- | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
- |
- = warning: this changes meaning in Rust 2021
- = note: for more information, see
-
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-lint.rs:28:22
- |
-LL | Box::new([1, 2]).into_iter();
- | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
- |
- = warning: this changes meaning in Rust 2021
- = note: for more information, see
-
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-lint.rs:31:19
- |
-LL | Box::new(big).into_iter();
- | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
- |
- = warning: this changes meaning in Rust 2021
- = note: for more information, see
-
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-lint.rs:34:25
- |
-LL | Box::new([0u8; 33]).into_iter();
- | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
- |
- = warning: this changes meaning in Rust 2021
- = note: for more information, see
-
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-lint.rs:38:31
- |
-LL | Box::new(Box::new(small)).into_iter();
- | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
- |
- = warning: this changes meaning in Rust 2021
- = note: for more information, see
-
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-lint.rs:41:32
- |
-LL | Box::new(Box::new([1, 2])).into_iter();
- | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
- |
- = warning: this changes meaning in Rust 2021
- = note: for more information, see
-
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-lint.rs:44:29
- |
-LL | Box::new(Box::new(big)).into_iter();
- | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
- |
- = warning: this changes meaning in Rust 2021
- = note: for more information, see
-
-warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021
- --> $DIR/into-iter-on-arrays-lint.rs:47:35
- |
-LL | Box::new(Box::new([0u8; 33])).into_iter();
- | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
- |
- = warning: this changes meaning in Rust 2021
- = note: for more information, see
-
-warning: 12 warnings emitted
+warning: 4 warnings emitted
diff --git a/tests/ui/lazy-type-alias/auto-trait-impls.rs b/tests/ui/lazy-type-alias/auto-trait-impls.rs
new file mode 100644
index 0000000000000..e5d9c988f0d5f
--- /dev/null
+++ b/tests/ui/lazy-type-alias/auto-trait-impls.rs
@@ -0,0 +1,53 @@
+//! Auto-trait impls whose self type is a free (lazy) type alias are checked against the
+//! type the alias *expands* to, not the alias itself. An alias resolving to an otherwise
+//! valid nominal type is accepted, just as if the underlying type had been written
+//! directly; an alias resolving to a problematic type is still rejected.
+//!
+//! Regression test for .
+
+#![feature(lazy_type_alias, auto_traits, negative_impls)]
+#![allow(incomplete_features)]
+
+struct Local;
+
+auto trait Marker {}
+
+// Aliases expanding to a local nominal type are accepted for both a cross-crate auto trait
+// (`Sync`) and a local one (`Marker`), exactly as if `Local` had been named directly.
+type ToLocal = Local;
+unsafe impl Sync for ToLocal {}
+impl Marker for ToLocal {}
+
+// Nested aliases are expanded transitively, so an alias chain ending in a local nominal type
+// is accepted as well.
+struct Inner;
+type Mid = Inner;
+type Nested = Mid;
+impl Marker for Nested {}
+
+// A generic alias instantiated with a local nominal type is accepted.
+struct Generic;
+type Id = T;
+impl Marker for Id {}
+
+// Negative auto-trait impls go through the same orphan check, so an alias to a local nominal
+// type is accepted for them too.
+struct Negated;
+type ToNeg = Negated;
+impl !Marker for ToNeg {}
+
+// An alias expanding to a reference is still rejected for the cross-crate auto trait. Using
+// `&'static NotSync` (with `NotSync: !Sync`) avoids overlapping std's `Send for &T` impl, so
+// the only error is the auto-trait nominal-type restriction.
+struct NotSync;
+impl !Sync for NotSync {}
+type ToRef = &'static NotSync;
+unsafe impl Send for ToRef {}
+//~^ ERROR cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `&'static NotSync`
+
+// An alias expanding to a trait object is still rejected for the local auto trait.
+type ToDyn = dyn Send;
+impl Marker for ToDyn {}
+//~^ ERROR traits with a default impl, like `Marker`, cannot be implemented for trait object `(dyn Send + 'static)`
+
+fn main() {}
diff --git a/tests/ui/lazy-type-alias/auto-trait-impls.stderr b/tests/ui/lazy-type-alias/auto-trait-impls.stderr
new file mode 100644
index 0000000000000..f2ab6db19fb2b
--- /dev/null
+++ b/tests/ui/lazy-type-alias/auto-trait-impls.stderr
@@ -0,0 +1,17 @@
+error[E0321]: traits with a default impl, like `Marker`, cannot be implemented for trait object `(dyn Send + 'static)`
+ --> $DIR/auto-trait-impls.rs:50:1
+ |
+LL | impl Marker for ToDyn {}
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: a trait object implements `Marker` if and only if `Marker` is one of the trait object's trait bounds
+
+error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `&'static NotSync`
+ --> $DIR/auto-trait-impls.rs:45:1
+ |
+LL | unsafe impl Send for ToRef {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0321`.
diff --git a/tests/ui/precondition-checks/transmute_copy.rs b/tests/ui/precondition-checks/transmute_copy.rs
new file mode 100644
index 0000000000000..160699a4c8ccd
--- /dev/null
+++ b/tests/ui/precondition-checks/transmute_copy.rs
@@ -0,0 +1,9 @@
+//@ run-crash
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: cannot transmute_copy if Dst is larger than Src
+
+fn main() {
+ unsafe {
+ let _unused: u64 = std::mem::transmute_copy(&1_u8);
+ }
+}
diff --git a/tests/ui/proc-macro/attributes-on-modules-fail.rs b/tests/ui/proc-macro/attributes-on-modules-fail.rs
index 80701523d49e6..6ad935d494458 100644
--- a/tests/ui/proc-macro/attributes-on-modules-fail.rs
+++ b/tests/ui/proc-macro/attributes-on-modules-fail.rs
@@ -17,31 +17,4 @@ type A = X; //~ ERROR cannot find type `X` in this scope
#[derive(Copy)] //~ ERROR `derive` may only be applied to `struct`s, `enum`s and `union`s
mod n {}
-#[empty_attr]
-mod module; //~ ERROR file modules in proc macro input are unstable
-
-#[empty_attr]
-mod outer {
- mod inner; //~ ERROR file modules in proc macro input are unstable
-
- mod inner_inline {} // OK
-}
-
-#[derive(Empty)]
-struct S {
- field: [u8; {
- #[path = "outer/inner.rs"]
- mod inner; //~ ERROR file modules in proc macro input are unstable
- mod inner_inline {} // OK
- 0
- }],
-}
-
-#[identity_attr]
-fn f() {
- #[path = "outer/inner.rs"]
- mod inner; //~ ERROR file modules in proc macro input are unstable
- mod inner_inline {} // OK
-}
-
fn main() {}
diff --git a/tests/ui/proc-macro/attributes-on-modules-fail.stderr b/tests/ui/proc-macro/attributes-on-modules-fail.stderr
index 1dc87fd0d5513..78593bf8de897 100644
--- a/tests/ui/proc-macro/attributes-on-modules-fail.stderr
+++ b/tests/ui/proc-macro/attributes-on-modules-fail.stderr
@@ -6,46 +6,6 @@ LL | #[derive(Copy)]
LL | mod n {}
| -------- not a `struct`, `enum` or `union`
-error[E0658]: file modules in proc macro input are unstable
- --> $DIR/attributes-on-modules-fail.rs:21:1
- |
-LL | mod module;
- | ^^^^^^^^^^^
- |
- = note: see issue #54727 for more information
- = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
- = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: file modules in proc macro input are unstable
- --> $DIR/attributes-on-modules-fail.rs:25:5
- |
-LL | mod inner;
- | ^^^^^^^^^^
- |
- = note: see issue #54727 for more information
- = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
- = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: file modules in proc macro input are unstable
- --> $DIR/attributes-on-modules-fail.rs:34:9
- |
-LL | mod inner;
- | ^^^^^^^^^^
- |
- = note: see issue #54727 for more information
- = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
- = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: file modules in proc macro input are unstable
- --> $DIR/attributes-on-modules-fail.rs:43:5
- |
-LL | mod inner;
- | ^^^^^^^^^^
- |
- = note: see issue #54727 for more information
- = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
- = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
error[E0425]: cannot find type `Y` in this scope
--> $DIR/attributes-on-modules-fail.rs:11:14
|
@@ -68,7 +28,7 @@ help: consider importing this struct
LL + use m::X;
|
-error: aborting due to 7 previous errors
+error: aborting due to 3 previous errors
-Some errors have detailed explanations: E0425, E0658, E0774.
+Some errors have detailed explanations: E0425, E0774.
For more information about an error, try `rustc --explain E0425`.
diff --git a/tests/ui/proc-macro/auxiliary/expect-modules.rs b/tests/ui/proc-macro/auxiliary/expect-modules.rs
new file mode 100644
index 0000000000000..57bd5f39f71a3
--- /dev/null
+++ b/tests/ui/proc-macro/auxiliary/expect-modules.rs
@@ -0,0 +1,41 @@
+/// Macros that assert that certain `mod` items are present in their input.
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_attribute]
+pub fn expect_mod_item(_attrs: TokenStream, item: TokenStream) -> TokenStream {
+ let s = item.to_string();
+
+ assert_eq!(s, "mod module;");
+
+ item
+}
+
+#[proc_macro_attribute]
+pub fn expect_mods_attr(_attrs: TokenStream, item: TokenStream) -> TokenStream {
+ let s = item.to_string();
+
+ assert_contains(&s, "mod attr_outlined;");
+ assert_contains(&s, "mod attr_inline {}");
+
+ item
+}
+
+#[proc_macro_derive(ExpectModsDerive)]
+pub fn expect_mods_derive(item: TokenStream) -> TokenStream {
+ let s = item.to_string();
+
+ assert_contains(&s, "mod derive_outlined;");
+ assert_contains(&s, "mod derive_inline {}");
+
+ TokenStream::new()
+}
+
+#[track_caller]
+fn assert_contains(s: &str, needle: &str) {
+ if !s.contains(needle) {
+ panic!("{needle:?} not found in:\n---\n{s}\n---\n");
+ }
+}
diff --git a/tests/ui/proc-macro/inner-attr-file-mod.rs b/tests/ui/proc-macro/inner-attr-file-mod.rs
index e592331c2c6db..535341957ade5 100644
--- a/tests/ui/proc-macro/inner-attr-file-mod.rs
+++ b/tests/ui/proc-macro/inner-attr-file-mod.rs
@@ -9,8 +9,7 @@ extern crate test_macros;
#[deny(unused_attributes)]
mod module_with_attrs;
-//~^ ERROR file modules in proc macro input are unstable
-//~| ERROR custom inner attributes are unstable
+//~^ ERROR custom inner attributes are unstable
fn main() {}
diff --git a/tests/ui/proc-macro/inner-attr-file-mod.stderr b/tests/ui/proc-macro/inner-attr-file-mod.stderr
index 92262284a00c4..a377313fc519e 100644
--- a/tests/ui/proc-macro/inner-attr-file-mod.stderr
+++ b/tests/ui/proc-macro/inner-attr-file-mod.stderr
@@ -18,16 +18,6 @@ LL | #![print_attr]
= help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-error[E0658]: file modules in proc macro input are unstable
- --> $DIR/inner-attr-file-mod.rs:11:1
- |
-LL | mod module_with_attrs;
- | ^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #54727 for more information
- = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
- = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
error[E0658]: custom inner attributes are unstable
--> $DIR/inner-attr-file-mod.rs:11:1
|
@@ -38,6 +28,6 @@ LL | mod module_with_attrs;
= help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/proc-macro/module.rs b/tests/ui/proc-macro/module.rs
index 5878f1b7ddd8a..f01bd5e50f0ab 100644
--- a/tests/ui/proc-macro/module.rs
+++ b/tests/ui/proc-macro/module.rs
@@ -1 +1,4 @@
-//@ ignore-auxiliary (used by `./attributes-on-modules-fail.rs`)
+//@ ignore-auxiliary (used by `./attributes-on-modules-fail.rs` and `modules-in-input.rs`)
+
+// An item that can be referenced to verify the module has been properly loaded.
+pub const fn hello() {}
diff --git a/tests/ui/proc-macro/modules-in-input.rs b/tests/ui/proc-macro/modules-in-input.rs
new file mode 100644
index 0000000000000..50b4dc9e40f44
--- /dev/null
+++ b/tests/ui/proc-macro/modules-in-input.rs
@@ -0,0 +1,47 @@
+//@ check-pass
+//@ proc-macro: expect-modules.rs
+//@ reference: macro.invocation.attr.mod
+
+// Verifies how module items are represented in proc macro input.
+
+#[macro_use]
+extern crate expect_modules;
+
+#[expect_modules::expect_mod_item]
+mod module;
+
+fn check() {
+ module::hello();
+}
+
+#[expect_modules::expect_mods_attr]
+mod outer {
+ #[path = "../module.rs"]
+ mod attr_outlined;
+ mod attr_inline {}
+
+ fn check() {
+ attr_outlined::hello();
+ }
+}
+
+#[expect_modules::expect_mods_attr]
+fn foo() {
+ #[path = "module.rs"]
+ mod attr_outlined;
+ mod attr_inline {}
+ attr_outlined::hello();
+}
+
+#[derive(expect_modules::ExpectModsDerive)]
+struct Foo(
+ [u8; {
+ #[path = "module.rs"]
+ mod derive_outlined;
+ mod derive_inline {}
+ derive_outlined::hello();
+ 0
+ }],
+);
+
+fn main() {}
diff --git a/tests/ui/proc-macro/unsafe-mod.rs b/tests/ui/proc-macro/unsafe-mod.rs
index f8453c2f62cd4..d9fbe7a8c5e1d 100644
--- a/tests/ui/proc-macro/unsafe-mod.rs
+++ b/tests/ui/proc-macro/unsafe-mod.rs
@@ -1,8 +1,6 @@
//@ run-pass
//@ proc-macro: macro-only-syntax.rs
-#![feature(proc_macro_hygiene)]
-
extern crate macro_only_syntax;
#[macro_only_syntax::expect_unsafe_mod]
diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs
index 9d87ff0bb938a..a425ab5fd1ced 100644
--- a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs
+++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs
@@ -1,3 +1,5 @@
+//@ check-pass
+
#![feature(reborrow)]
use std::marker::{CoerceShared, Reborrow};
@@ -25,7 +27,6 @@ struct AliasRef<'a> {
}
impl<'a> CoerceShared> for AliasMut<'a> {}
-//~^ ERROR
struct InnerLifetimeMut<'a> {
value: &'a mut &'static (),
diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr
deleted file mode 100644
index 63a96d65e176d..0000000000000
--- a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0277]: the trait bound `&'a mut u32: CoerceShared<&'a u32>` is not satisfied
- --> $DIR/coerce-shared-mut-ref-field-validation.rs:27:1
- |
-LL | impl<'a> CoerceShared> for AliasMut<'a> {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a u32>` is not implemented for `&'a mut u32`
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/reborrow/coerce-shared-normalize.rs b/tests/ui/reborrow/coerce-shared-normalize.rs
new file mode 100644
index 0000000000000..a212eaf27fccf
--- /dev/null
+++ b/tests/ui/reborrow/coerce-shared-normalize.rs
@@ -0,0 +1,56 @@
+//@ check-pass
+
+// Previously we didn't normalize the source type of `CoerceShared` when
+// checking its trait impls.
+// We also didn't normalize the types of fields in the wrappers.
+// So they fail the trait impl check in `coerce_shared_info` even if
+// the normalized types satisfy the requirements.
+
+#![feature(reborrow)]
+use std::marker::{CoerceShared, Reborrow};
+
+struct NonParam;
+
+trait HasAssoc<'a> {
+ type Assoc;
+}
+
+struct A;
+impl<'a> HasAssoc<'a> for A {
+ type Assoc = &'a mut NonParam;
+}
+type MutAlias<'a> = >::Assoc;
+
+struct B;
+impl<'a> HasAssoc<'a> for B {
+ type Assoc = &'a NonParam;
+}
+type RefAlias<'a> = >::Assoc;
+
+struct CustomMut<'a>(MutAlias<'a>);
+struct CustomRef<'a>(RefAlias<'a>);
+
+struct C;
+impl<'a> HasAssoc<'a> for C {
+ type Assoc = CustomMut<'a>;
+}
+type CustomMutAlias<'a> = >::Assoc;
+
+struct D;
+impl<'a> HasAssoc<'a> for D {
+ type Assoc = CustomRef<'a>;
+}
+type CustomRefAlias<'a> = >::Assoc;
+
+impl<'a> Clone for CustomRef<'a> {
+ fn clone(&self) -> Self {
+ Self(self.0)
+ }
+}
+impl<'a> Copy for CustomRef<'a> {}
+
+
+impl<'a> Reborrow for CustomMut<'a> {}
+impl<'a> CoerceShared> for CustomMutAlias<'a> {}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs b/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs
index b50345bb637c3..57a092a682aaf 100644
--- a/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs
+++ b/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs
@@ -1,3 +1,8 @@
+//! The orphan check expands a free alias used as the self type of an auto-trait impl, so an
+//! alias resolving to a tuple is accepted just like the tuple written directly. See #157756.
+
+//@ check-pass
+
#![feature(type_alias_impl_trait)]
#![feature(auto_traits)]
@@ -5,7 +10,6 @@ type Alias = (impl Sized, u8);
auto trait Trait {}
impl Trait for Alias {}
-//~^ ERROR traits with a default impl, like `Trait`, cannot be implemented for type alias `Alias`
#[define_opaque(Alias)]
fn _def() -> Alias {
diff --git a/tests/ui/type-alias-impl-trait/impl_for_weak_alias.stderr b/tests/ui/type-alias-impl-trait/impl_for_weak_alias.stderr
deleted file mode 100644
index a13e9eaab3add..0000000000000
--- a/tests/ui/type-alias-impl-trait/impl_for_weak_alias.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error[E0321]: traits with a default impl, like `Trait`, cannot be implemented for type alias `Alias`
- --> $DIR/impl_for_weak_alias.rs:7:1
- |
-LL | impl Trait for Alias {}
- | ^^^^^^^^^^^^^^^^^^^^
- |
- = note: a trait object implements `Trait` if and only if `Trait` is one of the trait object's trait bounds
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0321`.