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_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/backtrace b/library/backtrace index d902726a1dcdc..28ec93b503bf0 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit d902726a1dcdc1e1c66f73d1162181b5423c645b +Subproject commit 28ec93b503bf0410745bc3d571bf3dc1caac3019 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/std/src/backtrace.rs b/library/std/src/backtrace.rs index 99724e29e02b2..002b1575c8bfb 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -159,7 +159,10 @@ enum RawFrame { Fake, } -struct BacktraceSymbol { +/// A single symbol associated with a backtrace frame, which may include a +/// function name, filename, line number, and column number. +#[unstable(feature = "backtrace_internals_accessors", issue = "122899")] +pub struct BacktraceSymbol { name: Option>, filename: Option, lineno: Option, @@ -207,6 +210,41 @@ impl fmt::Debug for BacktraceFrame { } } +///NOTE: This is never intended to become stable. It doesn't have an ACP +/// Its meant to help us port the tests mentioned in issue #122899 +#[unstable(feature = "backtrace_internals_accessors", issue = "122899")] +impl BacktraceFrame { + /// Returns the instruction pointer associated with this frame. + pub fn ip(&self) -> *mut c_void { + self.frame.ip() + } + + /// Returns the symbols associated with this frame, if any. + pub fn symbols(&self) -> &[BacktraceSymbol] { + &self.symbols + } + + /// Returns the base address of the module this frame belongs to, if available. + pub fn module_base_address(&self) -> Option<*mut c_void> { + match &self.frame { + RawFrame::Actual(frame) => frame.module_base_address(), + #[cfg(test)] + RawFrame::Fake => None, + } + } +} + +///NOTE: This is never intended to become stable. It doesn't have an ACP +/// Its meant to help us port the tests mentioned in issue #122899 +impl BacktraceSymbol { + /// Returns the name of this symbol, if available. + #[unstable(feature = "backtrace_internals_accessors", issue = "122899")] + pub fn name(&self) -> Option<&[u8]> { + self.name.as_deref() + } +} + +#[unstable(feature = "backtrace_internals_accessors", issue = "122899")] impl fmt::Debug for BacktraceSymbol { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { // FIXME: improve formatting: https://github.com/rust-lang/rust/issues/65280 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/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/ui/README.md b/tests/ui/README.md index d342f393d0550..7e708ff845a1c 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1,12 +1,12 @@ # UI Test Suite Categories -This is a high-level summary of the organization of the UI test suite (`tests/ui/`). It is not intended to be *prescriptive*, but instead provide a quick survey of existing groupings. +This is a high-level summary of the organization of the UI test suite (`tests/ui/`). It is not intended to be _prescriptive_, but instead provide a quick survey of existing groupings. For now, only immediate subdirectories under `tests/ui/` are described, but these subdirectories can themselves include a `SUMMARY.md` to further describe their own organization and intent, should that be helpful. ## `tests/ui/abi` -These tests deal with *Application Binary Interfaces* (ABI), mostly relating to function name mangling (and the `#[no_mangle]` attribute), calling conventions, or compiler flags which affect ABI. +These tests deal with _Application Binary Interfaces_ (ABI), mostly relating to function name mangling (and the `#[no_mangle]` attribute), calling conventions, or compiler flags which affect ABI. Tests for unsupported ABIs can be made cross-platform by using the `extern "rust-invalid"` ABI, which is considered unsupported on every platform. @@ -212,13 +212,13 @@ See [Conditional compilation | Reference](https://doc.rust-lang.org/reference/co ## `tests/ui/check-cfg/`: Configuration Checks -Tests for the `--check-cfg` compiler mechanism for checking cfg configurations, for `#[cfg(..)]` and `cfg!(..)`. +Tests for the `--check-cfg` compiler mechanism for checking cfg configurations, for `#[cfg(..)]` and `cfg!(..)`. See [Checking conditional configurations | The rustc book](https://doc.rust-lang.org/rustc/check-cfg.html). ## `tests/ui/closure-expected-type/`: Closure type inference -Tests targeted at how we deduce the types of closure arguments. This process is a result of some heuristics which take into account the *expected type* we have alongside the *actual types* that we get from inputs. +Tests targeted at how we deduce the types of closure arguments. This process is a result of some heuristics which take into account the _expected type_ we have alongside the _actual types_ that we get from inputs. **FIXME**: Appears to have significant overlap with `tests/ui/closure_context` and `tests/ui/functions-closures/closure-expected-type`. Needs further investigation. @@ -286,7 +286,7 @@ This directory is actually for the standard library [`std::process::Command`](ht Some traits' implementation must be compared with their definition, checking for problems such as the implementation having stricter requirements (such as needing to implement `Copy`). -This subdirectory is *not* intended for comparison traits (`PartialEq`, `Eq`, `PartialOrd`, `Ord`). +This subdirectory is _not_ intended comparison traits (`PartialEq`, `Eq`, `PartialOrd`, `Ord`). ## `tests/ui/compile-flags/` @@ -700,6 +700,7 @@ This test category revolves around trait objects with `Sized` having illegal ope Tests on lifetime elision in impl function signatures. See [Lifetime elision | Nomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html). ## `tests/ui/impl-restriction/` + Tests for `#![feature(impl_restriction)]`. See [Tracking issue for restrictions #105077](https://github.com/rust-lang/rust/issues/105077). ## `tests/ui/impl-trait/` @@ -877,7 +878,7 @@ Tests on [AST lowering](https://rustc-dev-guide.rust-lang.org/ast-lowering.html) ## `tests/ui/lto/` -Exercise *Link-Time Optimization* (LTO), involving the flags `-C lto` or `-Z thinlto`. +Exercise _Link-Time Optimization_ (LTO), involving the flags `-C lto` or `-Z thinlto`. ## `tests/ui/lub-glb/`: LUB/GLB algorithm update @@ -948,6 +949,7 @@ Tests on moves (destructive moves). Broad category of tests on mutability, such as the `mut` keyword, borrowing a value as both immutable and mutable (and the associated error), or adding mutable references to `const` declarations. ## `tests/ui/mut-restriction/` + Tests for `#![feature(mut_restriction)]`. See [Tracking issue for restrictions #105077](https://github.com/rust-lang/rust/issues/105077). ## `tests/ui/namespace/` @@ -1357,7 +1359,7 @@ Tests for supertrait item shadowing (RFC 3624). ## `tests/ui/svh/`: Strict Version Hash -Tests on the *Strict Version Hash* (SVH, also known as the "crate hash"). +Tests on the _Strict Version Hash_ (SVH, also known as the "crate hash"). See [Strict Version Hash](https://rustc-dev-guide.rust-lang.org/backend/libs-and-metadata.html#strict-version-hash). diff --git a/tests/ui/backtrace/no_debuginfo/all_frames_have_module_base_address.rs b/tests/ui/backtrace/no_debuginfo/all_frames_have_module_base_address.rs new file mode 100644 index 0000000000000..35e66023a5b63 --- /dev/null +++ b/tests/ui/backtrace/no_debuginfo/all_frames_have_module_base_address.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -Cstrip=none -Cdebuginfo=none +//@ run-pass +#![feature(backtrace_frames)] +#![feature(backtrace_internals_accessors)] +use std::backtrace; + +fn main() { + let mut missing_base_addresses = 0; + let btrace = backtrace::Backtrace::force_capture(); + let frames = btrace.frames(); + for frame in frames { + if frame.module_base_address().is_none() { + missing_base_addresses += 1; + } + } + + if cfg!(windows) { + assert_eq!(missing_base_addresses, 0); + } +} diff --git a/tests/ui/backtrace/no_debuginfo/all_frames_have_symbols.rs b/tests/ui/backtrace/no_debuginfo/all_frames_have_symbols.rs new file mode 100644 index 0000000000000..cd076604166cc --- /dev/null +++ b/tests/ui/backtrace/no_debuginfo/all_frames_have_symbols.rs @@ -0,0 +1,45 @@ +//@ compile-flags: -Cstrip=none -Cdebuginfo=none +//@ run-pass +#![feature(backtrace_frames)] +#![feature(backtrace_internals_accessors)] +use std::backtrace; +fn main() { + let mut missing_symbols = 0; + let mut has_symbols = 0; + let btrace = backtrace::Backtrace::force_capture(); + let frames = btrace.frames(); + let mut missing_symbol_indices = Vec::new(); + for (i, frame) in frames.iter().enumerate() { + let mut any = false; + for sym in frame.symbols() { + if sym.name().is_some() { + any = true; + break; + } + } + if any { + has_symbols += 1; + } else if !frame.ip().is_null() { + missing_symbols += 1; + missing_symbol_indices.push(i); + } + } + + // FIXME(rust-lang/backtrace-rs#346) currently on MinGW we can't + // symbolize kernel32.dll and other system libraries, which means + // we miss the last few symbols. + if cfg!(windows) && cfg!(target_env = "gnu") { + assert!(missing_symbols < has_symbols && has_symbols > 4); + } else if cfg!(all(target_os = "linux", target_env = "gnu")) { + //NOTE: The reason we allow one missing symbol is because the frame for the + // `__libc_start_main` fn doesn't have a symbol. See the discussion in + // #152860 for more details. + assert!(missing_symbols < has_symbols && missing_symbols <= 1) + } else { + for i in missing_symbol_indices { + eprintln!("missing symbol for frame {i}: {:#?}", frames[i]); + } + eprintln!("Full erroneous backtrace: {:#?}", btrace); + assert_eq!(missing_symbols, 0); + } +} 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/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`.