diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index c6c93ed7f69ec..fa5041daa69e9 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -36,21 +36,21 @@ enum MulAddType { #[derive(Copy, Clone)] pub(crate) enum MinMax { - /// The IEEE `Minimum` operation - see `f32::minimum` etc + /// The IEEE-2019 `minimum` operation - see `f32::minimum` etc. /// In particular, `-0.0` is considered smaller than `+0.0` and /// if either input is NaN, the result is NaN. Minimum, - /// The IEEE `MinNum` operation - see `f32::min` etc + /// The IEEE-2008 `minNum` operation - see `f32::min` etc. /// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic, - /// and is one argument is NaN, the other one is returned. + /// and if one argument is NaN, the other one is returned. MinNum, - /// The IEEE `Maximum` operation - see `f32::maximum` etc + /// The IEEE-2019 `maximum` operation - see `f32::maximum` etc. /// In particular, `-0.0` is considered smaller than `+0.0` and /// if either input is NaN, the result is NaN. Maximum, - /// The IEEE `MaxNum` operation - see `f32::max` etc + /// The IEEE-2008 `maxNum` operation - see `f32::max` etc. /// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic, - /// and is one argument is NaN, the other one is returned. + /// and if one argument is NaN, the other one is returned. MaxNum, } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 47ecc71d8bf5b..6eefb2f48d127 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1586,9 +1586,10 @@ pub static BUILTIN_ATTRIBUTE_MAP: LazyLock> map }); -pub fn is_stable_diagnostic_attribute(sym: Symbol, _features: &Features) -> bool { +pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool { match sym { sym::on_unimplemented | sym::do_not_recommend => true, + sym::on_const => features.diagnostic_on_const(), _ => false, } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 813d2fced865d..cc1dfb72142f1 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -483,6 +483,8 @@ declare_features! ( (incomplete, deref_patterns, "1.79.0", Some(87121)), /// Allows deriving the From trait on single-field structs. (unstable, derive_from, "1.91.0", Some(144889)), + /// Allows giving non-const impls custom diagnostic messages if attempted to be used as const + (unstable, diagnostic_on_const, "CURRENT_RUSTC_VERSION", Some(143874)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), /// Allows `#[doc(masked)]`. diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index e124dfeb72840..beba0165549e0 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -802,6 +802,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); tcx.ensure_ok().associated_items(def_id); + check_diagnostic_attrs(tcx, def_id); if of_trait { let impl_trait_header = tcx.impl_trait_header(def_id); res = res.and( @@ -824,7 +825,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().predicates_of(def_id); tcx.ensure_ok().associated_items(def_id); let assoc_items = tcx.associated_items(def_id); - check_on_unimplemented(tcx, def_id); + check_diagnostic_attrs(tcx, def_id); for &assoc_item in assoc_items.in_definition_order() { match assoc_item.kind { @@ -1112,7 +1113,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), }) } -pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, def_id: LocalDefId) { +pub(super) fn check_diagnostic_attrs(tcx: TyCtxt<'_>, def_id: LocalDefId) { // an error would be reported if this fails. let _ = OnUnimplementedDirective::of_item(tcx, def_id.to_def_id()); } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 44177d4ae468c..91a20a12b6334 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -96,6 +96,9 @@ passes_deprecated_annotation_has_no_effect = passes_deprecated_attribute = deprecated attribute must be paired with either stable or unstable attribute +passes_diagnostic_diagnostic_on_const_only_for_trait_impls = + `#[diagnostic::on_const]` can only be applied to trait impls + passes_diagnostic_diagnostic_on_unimplemented_only_for_traits = `#[diagnostic::on_unimplemented]` can only be applied to trait definitions diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 58e55211c671c..cba600594cda4 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -23,8 +23,8 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId, Item, - ItemKind, MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target, + self as hir, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, Constness, FnSig, ForeignItem, HirId, + Item, ItemKind, MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target, TraitItem, find_attr, }; use rustc_macros::LintDiagnostic; @@ -55,6 +55,10 @@ use crate::{errors, fluent_generated as fluent}; #[diag(passes_diagnostic_diagnostic_on_unimplemented_only_for_traits)] struct DiagnosticOnUnimplementedOnlyForTraits; +#[derive(LintDiagnostic)] +#[diag(passes_diagnostic_diagnostic_on_const_only_for_trait_impls)] +struct DiagnosticOnConstOnlyForTraitImpls; + fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, @@ -294,6 +298,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::diagnostic, sym::on_unimplemented, ..] => { self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) } + [sym::diagnostic, sym::on_const, ..] => { + self.check_diagnostic_on_const(attr.span(), hir_id, target, item) + } [sym::thread_local, ..] => self.check_thread_local(attr, span, target), [sym::doc, ..] => self.check_doc_attrs( attr, @@ -517,6 +524,31 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// Checks if `#[diagnostic::on_const]` is applied to a trait impl + fn check_diagnostic_on_const( + &self, + attr_span: Span, + hir_id: HirId, + target: Target, + item: Option>, + ) { + if matches!(target, Target::Impl { of_trait: true }) { + match item.unwrap() { + ItemLike::Item(it) => match it.expect_impl().constness { + Constness::Const => {} + Constness::NotConst => return, + }, + ItemLike::ForeignItem => {} + } + } + self.tcx.emit_node_span_lint( + MISPLACED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnConstOnlyForTraitImpls, + ); + } + /// Checks if an `#[inline]` is applied to a function or a closure. fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) { match target { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 828759e2472f1..1222b40d8b367 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -690,10 +690,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments && namespace.ident.name == sym::diagnostic - && ![sym::on_unimplemented, sym::do_not_recommend].contains(&attribute.ident.name) + && ![sym::on_unimplemented, sym::do_not_recommend, sym::on_const] + .contains(&attribute.ident.name) { let typo_name = find_best_match_for_name( - &[sym::on_unimplemented, sym::do_not_recommend], + &[sym::on_unimplemented, sym::do_not_recommend, sym::on_const], attribute.ident.name, Some(5), ); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b3959c53a35e5..f2b13dad1fd90 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -878,6 +878,7 @@ symbols! { destructuring_assignment, diagnostic, diagnostic_namespace, + diagnostic_on_const, dialect, direct, discriminant_kind, @@ -1594,6 +1595,7 @@ symbols! { old_name, omit_gdb_pretty_printer_section, on, + on_const, on_unimplemented, opaque, opaque_module_name_placeholder: "", diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index c8cba30dde051..c5173179eecf2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -43,6 +43,7 @@ use super::{ }; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::TyCategory; +use crate::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use crate::error_reporting::traits::report_dyn_incompatibility; use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, CoroClosureNotFn}; use crate::infer::{self, InferCtxt, InferCtxtExt as _}; @@ -587,7 +588,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { - self.report_host_effect_error(bound_predicate.rebind(predicate), obligation.param_env, span) + self.report_host_effect_error(bound_predicate.rebind(predicate), &obligation, span) } ty::PredicateKind::Subtype(predicate) => { @@ -808,20 +809,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn report_host_effect_error( &self, predicate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, - param_env: ty::ParamEnv<'tcx>, + main_obligation: &PredicateObligation<'tcx>, span: Span, ) -> Diag<'a> { // FIXME(const_trait_impl): We should recompute the predicate with `[const]` // if it's `const`, and if it holds, explain that this bound only - // *conditionally* holds. If that fails, we should also do selection - // to drill this down to an impl or built-in source, so we can - // point at it and explain that while the trait *is* implemented, - // that implementation is not const. + // *conditionally* holds. let trait_ref = predicate.map_bound(|predicate| ty::TraitPredicate { trait_ref: predicate.trait_ref, polarity: ty::PredicatePolarity::Positive, }); let mut file = None; + let err_msg = self.get_standard_error_message( trait_ref, None, @@ -832,18 +831,21 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); let mut diag = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg); *diag.long_ty_path() = file; - if !self.predicate_may_hold(&Obligation::new( + let obligation = Obligation::new( self.tcx, ObligationCause::dummy(), - param_env, + main_obligation.param_env, trait_ref, - )) { + ); + if !self.predicate_may_hold(&obligation) { diag.downgrade_to_delayed_bug(); } - for candidate in self.find_similar_impl_candidates(trait_ref) { - let CandidateSimilarity::Exact { .. } = candidate.similarity else { continue }; - let impl_did = candidate.impl_def_id; - let trait_did = candidate.trait_ref.def_id; + + if let Ok(Some(ImplSource::UserDefined(impl_data))) = + SelectionContext::new(self).select(&obligation.with(self.tcx, trait_ref.skip_binder())) + { + let impl_did = impl_data.impl_def_id; + let trait_did = trait_ref.def_id(); let impl_span = self.tcx.def_span(impl_did); let trait_name = self.tcx.item_name(trait_did); @@ -865,6 +867,42 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { impl_span, format!("trait `{trait_name}` is implemented but not `const`"), ); + + let (condition_options, format_args) = self.on_unimplemented_components( + trait_ref, + main_obligation, + diag.long_ty_path(), + ); + + if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, impl_did) + { + let note = command.evaluate( + self.tcx, + predicate.skip_binder().trait_ref, + &condition_options, + &format_args, + ); + let OnUnimplementedNote { + message, + label, + notes, + parent_label, + append_const_msg: _, + } = note; + + if let Some(message) = message { + diag.primary_message(message); + } + if let Some(label) = label { + diag.span_label(impl_span, label); + } + for note in notes { + diag.note(note); + } + if let Some(parent_label) = parent_label { + diag.span_label(impl_span, parent_label); + } + } } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index a5cb374ea0eb1..0a6af42e122b6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -5,6 +5,7 @@ use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit}; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{AttrArgs, Attribute}; use rustc_macros::LintDiagnostic; @@ -103,7 +104,27 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if trait_pred.polarity() != ty::PredicatePolarity::Positive { return OnUnimplementedNote::default(); } + let (condition_options, format_args) = + self.on_unimplemented_components(trait_pred, obligation, long_ty_path); + if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, trait_pred.def_id()) + { + command.evaluate( + self.tcx, + trait_pred.skip_binder().trait_ref, + &condition_options, + &format_args, + ) + } else { + OnUnimplementedNote::default() + } + } + pub(crate) fn on_unimplemented_components( + &self, + trait_pred: ty::PolyTraitPredicate<'tcx>, + obligation: &PredicateObligation<'tcx>, + long_ty_path: &mut Option, + ) -> (ConditionOptions, FormatArgs<'tcx>) { let (def_id, args) = self .impl_similar_to(trait_pred, obligation) .unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args)); @@ -293,12 +314,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .collect(); let format_args = FormatArgs { this, trait_sugared, generic_args, item_context }; - - if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) { - command.evaluate(self.tcx, trait_pred.trait_ref, &condition_options, &format_args) - } else { - OnUnimplementedNote::default() - } + (condition_options, format_args) } } @@ -325,7 +341,7 @@ pub struct OnUnimplementedDirective { } /// For the `#[rustc_on_unimplemented]` attribute -#[derive(Default)] +#[derive(Default, Debug)] pub struct OnUnimplementedNote { pub message: Option, pub label: Option, @@ -562,17 +578,21 @@ impl<'tcx> OnUnimplementedDirective { } pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result, ErrorGuaranteed> { - if !tcx.is_trait(item_def_id) { + let attr = if tcx.is_trait(item_def_id) { + sym::on_unimplemented + } else if let DefKind::Impl { of_trait: true } = tcx.def_kind(item_def_id) { + sym::on_const + } else { // It could be a trait_alias (`trait MyTrait = SomeOtherTrait`) // or an implementation (`impl MyTrait for Foo {}`) // // We don't support those. return Ok(None); - } + }; if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) { return Self::parse_attribute(attr, false, tcx, item_def_id); } else { - tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented]) + tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, attr]) .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose()) .try_fold(None, |aggr: Option, directive| { let directive = directive?; diff --git a/library/Cargo.lock b/library/Cargo.lock index 8f60cea459c7d..accbbe9d236ff 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "moto-rt" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058a2807a30527bee4c30df7ababe971cdde94372d4dbd1ff145bb403381436c" +checksum = "0bf4bc387d3b3502cb92c09ec980cca909b94978e144c61da8319ecf4bc8d031" dependencies = [ "rustc-std-workspace-alloc", "rustc-std-workspace-core", diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 9d99b05b38d69..2115c5c9a85d8 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2947,61 +2947,77 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize); #[rustc_intrinsic] pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize); -/// Returns the minimum (IEEE 754-2008 minNum) of two `f16` values. +/// Returns the minimum of two `f16` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f16::min`] +/// The stabilized version of this intrinsic is [`f16::min`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn minnumf16(x: f16, y: f16) -> f16; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f32` values. +/// Returns the minimum of two `f32` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f32::min`] +/// The stabilized version of this intrinsic is [`f32::min`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn minnumf32(x: f32, y: f32) -> f32; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f64` values. +/// Returns the minimum of two `f64` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f64::min`] +/// The stabilized version of this intrinsic is [`f64::min`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn minnumf64(x: f64, y: f64) -> f64; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f128` values. +/// Returns the minimum of two `f128` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f128::min`] +/// The stabilized version of this intrinsic is [`f128::min`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn minnumf128(x: f128, y: f128) -> f128; -/// Returns the minimum (IEEE 754-2019 minimum) of two `f16` values. +/// Returns the minimum of two `f16` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3022,7 +3038,11 @@ pub const fn minimumf16(x: f16, y: f16) -> f16 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f32` values. +/// Returns the minimum of two `f32` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3043,7 +3063,11 @@ pub const fn minimumf32(x: f32, y: f32) -> f32 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f64` values. +/// Returns the minimum of two `f64` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3064,7 +3088,11 @@ pub const fn minimumf64(x: f64, y: f64) -> f64 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f128` values. +/// Returns the minimum of two `f128` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3085,61 +3113,77 @@ pub const fn minimumf128(x: f128, y: f128) -> f128 { } } -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f16` values. +/// Returns the maximum of two `f16` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f16::max`] +/// The stabilized version of this intrinsic is [`f16::max`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn maxnumf16(x: f16, y: f16) -> f16; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f32` values. +/// Returns the maximum of two `f32` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f32::max`] +/// The stabilized version of this intrinsic is [`f32::max`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn maxnumf32(x: f32, y: f32) -> f32; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f64` values. +/// Returns the maximum of two `f64` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f64::max`] +/// The stabilized version of this intrinsic is [`f64::max`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn maxnumf64(x: f64, y: f64) -> f64; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f128` values. +/// Returns the maximum of two `f128` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f128::max`] +/// The stabilized version of this intrinsic is [`f128::max`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn maxnumf128(x: f128, y: f128) -> f128; -/// Returns the maximum (IEEE 754-2019 maximum) of two `f16` values. +/// Returns the maximum of two `f16` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3159,7 +3203,11 @@ pub const fn maximumf16(x: f16, y: f16) -> f16 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f32` values. +/// Returns the maximum of two `f32` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3179,7 +3227,11 @@ pub const fn maximumf32(x: f32, y: f32) -> f32 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f64` values. +/// Returns the maximum of two `f64` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3199,7 +3251,11 @@ pub const fn maximumf64(x: f64, y: f64) -> f64 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f128` values. +/// Returns the maximum of two `f128` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index ef85e36900869..a1c7990f2311d 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -149,6 +149,7 @@ #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(derive_const)] +#![feature(diagnostic_on_const)] #![feature(doc_cfg)] #![feature(doc_notable_trait)] #![feature(extern_types)] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 3507d1a0a9a8c..aee28c4590c29 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1128,7 +1128,6 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// /// let mut dst = [MaybeUninit::uninit(); 32]; @@ -1140,8 +1139,6 @@ impl [MaybeUninit] { /// ``` /// /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// /// let mut vec = Vec::with_capacity(32); /// let src = [0; 16]; /// @@ -1157,7 +1154,8 @@ impl [MaybeUninit] { /// ``` /// /// [`write_clone_of_slice`]: slice::write_clone_of_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] pub const fn write_copy_of_slice(&mut self, src: &[T]) -> &mut [T] where T: Copy, @@ -1188,7 +1186,6 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// /// let mut dst = [const { MaybeUninit::uninit() }; 5]; @@ -1203,8 +1200,6 @@ impl [MaybeUninit] { /// ``` /// /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// /// let mut vec = Vec::with_capacity(32); /// let src = ["rust", "is", "a", "pretty", "cool", "language"].map(|s| s.to_string()); /// @@ -1220,7 +1215,7 @@ impl [MaybeUninit] { /// ``` /// /// [`write_copy_of_slice`]: slice::write_copy_of_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] pub fn write_clone_of_slice(&mut self, src: &[T]) -> &mut [T] where T: Clone, @@ -1415,7 +1410,7 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)] + /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] /// use std::mem::MaybeUninit; /// /// let uninit = [MaybeUninit::new(0x1234u16), MaybeUninit::new(0x5678u16)]; @@ -1442,7 +1437,7 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)] + /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] /// use std::mem::MaybeUninit; /// /// let mut uninit = [MaybeUninit::::uninit(), MaybeUninit::::uninit()]; diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 2cf06b6d6a35a..c83455c5870bb 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -709,6 +709,7 @@ impl f128 { /// let y = 2.0f128; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f128::NAN), x); /// # } /// ``` #[inline] @@ -736,6 +737,7 @@ impl f128 { /// let y = 2.0f128; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f128::NAN), x); /// # } /// ``` #[inline] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 51f803672e5c6..e7da93cc7318d 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -701,6 +701,7 @@ impl f16 { /// let y = 2.0f16; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f16::NAN), x); /// # } /// ``` #[inline] @@ -727,6 +728,7 @@ impl f16 { /// let y = 2.0f16; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f16::NAN), x); /// # } /// ``` #[inline] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 3070e1dedbe43..0e562c0c8fe53 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -908,6 +908,7 @@ impl f32 { /// let y = 2.0f32; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f32::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -930,6 +931,7 @@ impl f32 { /// let y = 2.0f32; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f32::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index dc8ccc551b2da..04f26affeae55 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -926,6 +926,7 @@ impl f64 { /// let y = 2.0_f64; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f64::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -948,6 +949,7 @@ impl f64 { /// let y = 2.0_f64; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f64::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 1d108cb0cf4a9..e3e843f955bd2 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -479,6 +479,76 @@ macro_rules! uint_impl { intrinsics::bswap(self as $ActualT) as Self } + /// Returns an integer with the bit locations specified by `mask` packed + /// contiguously into the least significant bits of the result. + /// ``` + /// #![feature(uint_gather_scatter_bits)] + #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1011_1100;")] + /// + /// assert_eq!(n.gather_bits(0b0010_0100), 0b0000_0011); + /// assert_eq!(n.gather_bits(0xF0), 0b0000_1011); + /// ``` + #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn gather_bits(self, mut mask: Self) -> Self { + let mut bit_position = 1; + let mut result = 0; + + // Iterate through the mask bits, unsetting the lowest bit after + // each iteration. We fill the bits in the result starting from the + // least significant bit. + while mask != 0 { + // Find the next lowest set bit in the mask + let next_mask_bit = mask.isolate_lowest_one(); + + // Retrieve the masked bit and if present, set it in the result + let src_bit = (self & next_mask_bit) != 0; + result |= if src_bit { bit_position } else { 0 }; + + // Unset lowest set bit in the mask, prepare next position to set + mask ^= next_mask_bit; + bit_position <<= 1; + } + + result + } + + /// Returns an integer with the least significant bits of `self` + /// distributed to the bit locations specified by `mask`. + /// ``` + /// #![feature(uint_gather_scatter_bits)] + #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1010_1101;")] + /// + /// assert_eq!(n.scatter_bits(0b0101_0101), 0b0101_0001); + /// assert_eq!(n.scatter_bits(0xF0), 0b1101_0000); + /// ``` + #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn scatter_bits(mut self, mut mask: Self) -> Self { + let mut result = 0; + + // Iterate through the mask bits, unsetting the lowest bit after + // each iteration and right-shifting `self` by one to get the next + // bit into the least significant bit position. + while mask != 0 { + // Find the next bit position to potentially set + let next_mask_bit = mask.isolate_lowest_one(); + + // If bit is set, deposit it at the masked bit position + result |= if (self & 1) != 0 { next_mask_bit } else { 0 }; + + // Unset lowest set bit in the mask, shift in next `self` bit + mask ^= next_mask_bit; + self >>= 1; + } + + result + } + /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, /// second least-significant bit becomes second most-significant bit, etc. /// diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 84a6982d56805..2860bf0a6e69a 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1567,6 +1567,10 @@ impl *const [T; N] { /// Pointer equality is by address, as produced by the [`<*const T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -1577,10 +1581,18 @@ impl PartialEq for *const T { /// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for *const T {} /// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -1597,6 +1609,10 @@ impl Ord for *const T { /// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index ea0514f405f1e..29fe77390a16b 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -2520,6 +2520,10 @@ pub fn hash(hashee: *const T, into: &mut S) { } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for F { #[inline] fn eq(&self, other: &Self) -> bool { @@ -2527,9 +2531,17 @@ impl PartialEq for F { } } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for F {} #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for F { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -2537,6 +2549,10 @@ impl PartialOrd for F { } } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for F { #[inline] fn cmp(&self, other: &Self) -> Ordering { diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 85d54b4d3b9b3..f8dc6ef7ed71f 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -2000,6 +2000,10 @@ impl *mut [T; N] { /// Pointer equality is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for *mut T { #[inline(always)] #[allow(ambiguous_wide_pointer_comparisons)] @@ -2010,10 +2014,18 @@ impl PartialEq for *mut T { /// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for *mut T {} /// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for *mut T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -2030,6 +2042,10 @@ impl Ord for *mut T { /// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for *mut T { #[inline(always)] #[allow(ambiguous_wide_pointer_comparisons)] diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 7053ae86e732f..a2fbf6ead6461 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -139,7 +139,7 @@ impl<'a, T> Iter<'a, T> { } } -iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, { +iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, each_ref, { fn is_sorted_by(self, mut compare: F) -> bool where Self: Sized, @@ -368,7 +368,7 @@ impl AsRef<[T]> for IterMut<'_, T> { // } // } -iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, as_mut, {}} +iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, as_mut, each_mut, {}} /// An internal abstraction over the splitting iterators, so that /// splitn, splitn_mut etc can be implemented once. diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index c46b7c797aab6..236bdf9d89cae 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -68,6 +68,7 @@ macro_rules! iterator { $raw_mut:tt, {$( $mut_:tt )?}, $into_ref:ident, + $array_ref:ident, {$($extra:tt)*} ) => { impl<'a, T> $name<'a, T> { @@ -190,6 +191,29 @@ macro_rules! iterator { } } + fn next_chunk(&mut self) -> Result<[$elem; N], crate::array::IntoIter<$elem, N>> { + if T::IS_ZST { + return crate::array::iter_next_chunk(self); + } + let len = len!(self); + if len >= N { + // SAFETY: we are just getting an array of [T; N] and moving the pointer over a little + let r = unsafe { self.post_inc_start(N).cast_array().$into_ref() } + .$array_ref(); // must convert &[T; N] to [&T; N] + Ok(r) + } else { + // cant use $array_ref because theres no builtin for &mut [MU; N] -> [&mut MU; N] + // cant use copy_nonoverlapping as the $elem is of type &{mut} T instead of T + let mut a = [const { crate::mem::MaybeUninit::<$elem>::uninit() }; N]; + for into in (&mut a).into_iter().take(len) { + // SAFETY: take(n) limits to remainder (slice produces worse codegen) + into.write(unsafe { self.post_inc_start(1).$into_ref() }); + } + // SAFETY: we just initialized elements 0..len + unsafe { Err(crate::array::IntoIter::new_unchecked(a, 0..len)) } + } + } + #[inline] fn size_hint(&self) -> (usize, Option) { let exact = len!(self); diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index e190536abcf9f..48982d71e8137 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -81,7 +81,6 @@ #![feature(lazy_get)] #![feature(maybe_uninit_fill)] #![feature(maybe_uninit_uninit_array_transpose)] -#![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(never_type)] #![feature(new_range_api)] @@ -115,6 +114,7 @@ #![feature(try_find)] #![feature(try_trait_v2)] #![feature(uint_bit_width)] +#![feature(uint_gather_scatter_bits)] #![feature(unsize)] #![feature(unwrap_infallible)] // tidy-alphabetical-end diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index b89a371efcc25..7f3e27e9c446c 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -127,6 +127,52 @@ macro_rules! uint_module { assert_eq_const_safe!($T: _1.swap_bytes(), _1); } + fn test_gather_bits() { + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0011), 0b_0001); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0110), 0b_0010); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_1100), 0b_0001); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0001_1000), 0b_0000); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0011_0000), 0b_0010); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0110_0000), 0b_0001); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b1100_0000), 0b_0010); + + assert_eq_const_safe!($T: A.gather_bits(_0), 0); + assert_eq_const_safe!($T: B.gather_bits(_0), 0); + assert_eq_const_safe!($T: C.gather_bits(_0), 0); + assert_eq_const_safe!($T: _0.gather_bits(A), 0); + assert_eq_const_safe!($T: _0.gather_bits(B), 0); + assert_eq_const_safe!($T: _0.gather_bits(C), 0); + + assert_eq_const_safe!($T: A.gather_bits(_1), A); + assert_eq_const_safe!($T: B.gather_bits(_1), B); + assert_eq_const_safe!($T: C.gather_bits(_1), C); + assert_eq_const_safe!($T: _1.gather_bits(0b0010_0001), 0b0000_0011); + assert_eq_const_safe!($T: _1.gather_bits(0b0010_1100), 0b0000_0111); + assert_eq_const_safe!($T: _1.gather_bits(0b0111_1001), 0b0001_1111); + } + + fn test_scatter_bits() { + assert_eq_const_safe!($T: $T::scatter_bits(0b1111, 0b1001_0110), 0b1001_0110); + assert_eq_const_safe!($T: $T::scatter_bits(0b0001, 0b1001_0110), 0b0000_0010); + assert_eq_const_safe!($T: $T::scatter_bits(0b0010, 0b1001_0110), 0b0000_0100); + assert_eq_const_safe!($T: $T::scatter_bits(0b0100, 0b1001_0110), 0b0001_0000); + assert_eq_const_safe!($T: $T::scatter_bits(0b1000, 0b1001_0110), 0b1000_0000); + + assert_eq_const_safe!($T: A.scatter_bits(_0), 0); + assert_eq_const_safe!($T: B.scatter_bits(_0), 0); + assert_eq_const_safe!($T: C.scatter_bits(_0), 0); + assert_eq_const_safe!($T: _0.scatter_bits(A), 0); + assert_eq_const_safe!($T: _0.scatter_bits(B), 0); + assert_eq_const_safe!($T: _0.scatter_bits(C), 0); + + assert_eq_const_safe!($T: A.scatter_bits(_1), A); + assert_eq_const_safe!($T: B.scatter_bits(_1), B); + assert_eq_const_safe!($T: C.scatter_bits(_1), C); + assert_eq_const_safe!($T: _1.scatter_bits(A), A); + assert_eq_const_safe!($T: _1.scatter_bits(B), B); + assert_eq_const_safe!($T: _1.scatter_bits(C), C); + } + fn test_reverse_bits() { assert_eq_const_safe!($T: A.reverse_bits().reverse_bits(), A); assert_eq_const_safe!($T: B.reverse_bits().reverse_bits(), B); diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml index 0042a6e8ece58..3a4840a57334b 100644 --- a/library/proc_macro/Cargo.toml +++ b/library/proc_macro/Cargo.toml @@ -14,3 +14,6 @@ rustc-literal-escaper = { version = "0.0.5", features = ["rustc-dep-of-std"] } [features] default = ["rustc-dep-of-std"] rustc-dep-of-std = [] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)'] } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 4efdfcad924b5..108ef7bce81a2 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -22,7 +22,7 @@ #![feature(staged_api)] #![feature(allow_internal_unstable)] #![feature(decl_macro)] -#![feature(maybe_uninit_write_slice)] +#![cfg_attr(bootstrap, feature(maybe_uninit_write_slice))] #![feature(negative_impls)] #![feature(panic_can_unwind)] #![feature(restricted_std)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 32f166e112e99..8df87124245bc 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -349,7 +349,6 @@ #![feature(lazy_get)] #![feature(maybe_uninit_array_assume_init)] #![feature(maybe_uninit_slice)] -#![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 5c0ac526a36c9..dbcf2684c6fb1 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1206,6 +1206,30 @@ impl Command { pub fn get_current_dir(&self) -> Option<&Path> { self.inner.get_current_dir() } + + /// Returns whether the environment will be cleared for the child process. + /// + /// This returns `true` if [`Command::env_clear`] was called, and `false` otherwise. + /// When `true`, the child process will not inherit any environment variables from + /// its parent process. + /// + /// # Examples + /// + /// ``` + /// #![feature(command_resolved_envs)] + /// use std::process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// assert_eq!(cmd.get_env_clear(), false); + /// + /// cmd.env_clear(); + /// assert_eq!(cmd.get_env_clear(), true); + /// ``` + #[must_use] + #[unstable(feature = "command_resolved_envs", issue = "149070")] + pub fn get_env_clear(&self) -> bool { + self.inner.get_env_clear() + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/sys/pal/motor/mod.rs b/library/std/src/sys/pal/motor/mod.rs index c64f8ff7a8a83..32f95df6ad082 100644 --- a/library/std/src/sys/pal/motor/mod.rs +++ b/library/std/src/sys/pal/motor/mod.rs @@ -58,16 +58,27 @@ pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { } match code as moto_rt::ErrorCode /* u16 */ { - E_ALREADY_IN_USE => ErrorKind::AlreadyExists, - E_INVALID_FILENAME => ErrorKind::InvalidFilename, + E_UNSPECIFIED => ErrorKind::Uncategorized, + E_UNKNOWN => ErrorKind::Uncategorized, + E_NOT_READY => ErrorKind::WouldBlock, + E_NOT_IMPLEMENTED => ErrorKind::Unsupported, + E_VERSION_TOO_HIGH => ErrorKind::Unsupported, + E_VERSION_TOO_LOW => ErrorKind::Unsupported, + E_INVALID_ARGUMENT => ErrorKind::InvalidInput, + E_OUT_OF_MEMORY => ErrorKind::OutOfMemory, + E_NOT_ALLOWED => ErrorKind::PermissionDenied, E_NOT_FOUND => ErrorKind::NotFound, + E_INTERNAL_ERROR => ErrorKind::Other, E_TIMED_OUT => ErrorKind::TimedOut, - E_NOT_IMPLEMENTED => ErrorKind::Unsupported, - E_FILE_TOO_LARGE => ErrorKind::FileTooLarge, + E_ALREADY_IN_USE => ErrorKind::AlreadyExists, E_UNEXPECTED_EOF => ErrorKind::UnexpectedEof, - E_INVALID_ARGUMENT => ErrorKind::InvalidInput, - E_NOT_READY => ErrorKind::WouldBlock, + E_INVALID_FILENAME => ErrorKind::InvalidFilename, + E_NOT_A_DIRECTORY => ErrorKind::NotADirectory, + E_BAD_HANDLE => ErrorKind::InvalidInput, + E_FILE_TOO_LARGE => ErrorKind::FileTooLarge, E_NOT_CONNECTED => ErrorKind::NotConnected, + E_STORAGE_FULL => ErrorKind::StorageFull, + E_INVALID_DATA => ErrorKind::InvalidData, _ => crate::io::ErrorKind::Uncategorized, } } diff --git a/library/std/src/sys/process/motor.rs b/library/std/src/sys/process/motor.rs index 9060902bc3d20..949a9d4942901 100644 --- a/library/std/src/sys/process/motor.rs +++ b/library/std/src/sys/process/motor.rs @@ -98,6 +98,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(Path::new) } diff --git a/library/std/src/sys/process/uefi.rs b/library/std/src/sys/process/uefi.rs index 11c8b682bb9bc..8d44292611bcb 100644 --- a/library/std/src/sys/process/uefi.rs +++ b/library/std/src/sys/process/uefi.rs @@ -83,6 +83,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { None } diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index 1d5909e99bacc..44d54aaf51512 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -263,6 +263,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes()))) } diff --git a/library/std/src/sys/process/unsupported.rs b/library/std/src/sys/process/unsupported.rs index 636465b68e541..2dfc676ec0059 100644 --- a/library/std/src/sys/process/unsupported.rs +++ b/library/std/src/sys/process/unsupported.rs @@ -86,6 +86,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(|cs| Path::new(cs)) } diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index 7d58093c54bbf..6e8be21a1fa6d 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -250,6 +250,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(Path::new) } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 535e6a510ca68..c5006912c97ac 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -228,9 +228,8 @@ pub fn prepare_tool_cargo( // own copy cargo.env("LZMA_API_STATIC", "1"); - // Note that `miri` always uses jemalloc. As such, there is no checking of the jemalloc build flag. // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the compile build step. - if env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { + if builder.config.jemalloc(target) && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { // Build jemalloc on AArch64 with support for page sizes up to 64K // See: https://github.com/rust-lang/rust/pull/135081 if target.starts_with("aarch64") { diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index ed280addb3b84..37ad5f09897fa 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -599,6 +599,6 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ ChangeInfo { change_id: 148795, severity: ChangeSeverity::Info, - summary: "New options `rust.rustflags` for all targets and `rustflags` par target that will pass specified flags to rustc for all stages. Target specific flags override global `rust.rustflags` ones.", + summary: "New options `rust.rustflags` for all targets and per-target `rustflags` that will pass specified flags to rustc for all stages. Target-specific flags override global `rust.rustflags` ones.", }, ]; diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 23f30e8dab3b3..111fe89e7eb0a 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -445,6 +445,7 @@ pub fn check(path: &Path, tidy_ctx: TidyCtx) { let mut comment_block: Option<(usize, usize)> = None; let is_test = file.components().any(|c| c.as_os_str() == "tests") || file.file_stem().unwrap() == "tests"; + let is_codegen_test = is_test && file.components().any(|c| c.as_os_str() == "codegen-llvm"); let is_this_file = file.ends_with(this_file) || this_file.ends_with(file); let is_test_for_this_file = is_test && file.parent().unwrap().ends_with(this_file.with_extension("")); @@ -488,6 +489,11 @@ pub fn check(path: &Path, tidy_ctx: TidyCtx) { ) } + if is_codegen_test && trimmed.contains("CHECK") && trimmed.ends_with(": br") { + err("`CHECK: br` and `CHECK-NOT: br` in codegen tests are fragile to false \ + positives in mangled symbols. Try using `br {{.*}}` instead.") + } + if !under_rustfmt && line.chars().count() > max_columns && !long_line_is_ok(&extension, is_error_code, max_columns, line) diff --git a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-2.rs b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-2.rs new file mode 100644 index 0000000000000..e355602f2c3f1 --- /dev/null +++ b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-2.rs @@ -0,0 +1,481 @@ +//@ revisions: all strong none missing +//@ assembly-output: emit-asm +//@ ignore-apple slightly different policy on stack protection of arrays +//@ ignore-msvc stack check code uses different function names +//@ ignore-nvptx64 stack protector is not supported +//@ ignore-wasm32 +//@ [all] compile-flags: -Z stack-protector=all +//@ [strong] compile-flags: -Z stack-protector=strong +//@ [none] compile-flags: -Z stack-protector=none +//@ compile-flags: -C opt-level=2 -Z merge-functions=disabled + +#![crate_type = "lib"] +#![allow(internal_features)] +#![feature(unsized_fn_params)] + +extern "C" { + fn strcpy(dest: *mut u8, src: *const u8) -> *mut u8; + fn printf(fmt: *const u8, ...) -> i32; + fn funcall(p: *mut i32); + fn funcall2(p: *mut *mut i32); + fn funfloat(p: *mut f64); + fn funfloat2(p: *mut *mut f64); + fn testi_aux() -> f64; + fn getp() -> *mut i32; + fn dummy(_: ...) -> i32; + + static STR: [u8; 1]; +} + +extern "C-unwind" { + fn except(p: *mut i32); +} + +#[repr(C)] +struct Pair { + a: i32, + b: i32, +} + +#[repr(C)] +struct Nest { + first: Pair, + second: Pair, +} + +// test1: array of [16 x i8] +// CHECK-LABEL: test1{{:|\[}} +#[no_mangle] +pub fn test1(a: *const u8) { + let mut buf: [u8; 16] = [0; 16]; + + unsafe { + strcpy(buf.as_mut_ptr(), a); + printf(STR.as_ptr(), buf.as_ptr()); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test2: array [4 x i8] +// CHECK-LABEL: test2{{:|\[}} +#[no_mangle] +pub fn test2(a: *const u8) { + let mut buf: [u8; 4] = [0; 4]; + + unsafe { + strcpy(buf.as_mut_ptr(), a); + printf(STR.as_ptr(), buf.as_ptr()); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test3: no arrays / no nested arrays +// CHECK-LABEL: test3{{:|\[}} +#[no_mangle] +pub fn test3(a: *const u8) { + unsafe { + printf(STR.as_ptr(), a); + } + + // all: __stack_chk_fail + // strong-NOT: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test4: Address-of local taken (j = &a) +// CHECK-LABEL: test4{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test4() { + let mut a: i32 = 0; + + let mut j: *mut i32 = std::ptr::null_mut(); + + let tmp = std::ptr::read_volatile(&a); + let tmp2 = tmp.wrapping_add(1); + std::ptr::write_volatile(&mut a, tmp2); + + std::ptr::write_volatile(&mut j, &mut a); + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test5: PtrToInt Cast +// CHECK-LABEL: test5{{:|\[}} +#[no_mangle] +pub fn test5(a: i32) { + let ptr_val: usize = &a as *const i32 as usize; + + unsafe { + printf(STR.as_ptr(), ptr_val); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test6: Passing addr-of to function call +// CHECK-LABEL: test6{{:|\[}} +#[no_mangle] +pub fn test6(mut b: i32) { + unsafe { + funcall(&mut b as *mut i32); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test7: Addr-of in select instruction +// CHECK-LABEL: test7{{:|\[}} +#[no_mangle] +pub fn test7() { + let x: f64; + + unsafe { + let call = testi_aux(); + x = call; + + let y: *const f64 = if call > 0.0 { &x as *const f64 } else { std::ptr::null() }; + + printf(STR.as_ptr(), y); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test8: Addr-of in phi instruction +// CHECK-LABEL: test8{{:|\[}} +#[no_mangle] +pub fn test8() { + let mut _x: f64; + + unsafe { + let call = testi_aux(); + _x = call; + + let y: *const f64; + + if call > 3.14 { + let call1 = testi_aux(); + _x = call1; + y = std::ptr::null(); + } else { + if call > 1.0 { + y = &_x; + } else { + y = std::ptr::null(); + } + } + + printf(STR.as_ptr(), y); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test9: Addr-of struct element(GEP followed by store) +// CHECK-LABEL: test9{{:|\[}} +#[no_mangle] +pub fn test9() { + let mut c = Pair { a: 0, b: 0 }; + let b: *mut i32; + + unsafe { + let y: *mut i32 = &mut c.b; + + b = y; + + printf(STR.as_ptr(), b); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test10: Addr-of struct element, GEP followed by ptrtoint +// CHECK-LABEL: test10{{:|\[}} +#[no_mangle] +pub fn test10() { + let mut c = Pair { a: 0, b: 0 }; + + unsafe { + let y: *mut i32 = &mut c.b; + + let addr: i64 = y as i64; + + printf(STR.as_ptr(), addr); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test11: Addr-of struct element, GEP followed by callinst +// CHECK-LABEL: test11{{:|\[}} +#[no_mangle] +pub fn test11() { + let mut c = Pair { a: 0, b: 0 }; + + unsafe { + let y: *mut i32 = &mut c.b; + + printf(STR.as_ptr(), y); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test12: Addr-of a local, optimized into a GEP (e.g., &a - 12) +// CHECK-LABEL: test12{{:|\[}} +#[no_mangle] +pub fn test12() { + let mut a: i32 = 0; + + unsafe { + let add_ptr = (&mut a as *mut i32).offset(-12); + + printf(STR.as_ptr(), add_ptr); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test13: Addr-of a local cast to a ptr of a different type +// (e.g., int a; ... ; ptr b = &a;) +// CHECK-LABEL: test13{{:|\[}} +#[no_mangle] +pub fn test13() { + let mut a: i32 = 0; + + unsafe { + let mut b: *mut i32 = std::ptr::null_mut(); + + std::ptr::write_volatile(&mut b, &mut a); // avoid ptr b from optimization + let tmp = std::ptr::read_volatile(&b); + + printf(STR.as_ptr(), tmp); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test14: Addr-of a local cast to a ptr of a different type (optimized) +// (e.g., int a; ... ; ptr b = &a;) +// CHECK-LABEL: test14{{:|\[}} +#[no_mangle] +pub fn test14() { + let a: i32 = 0; + unsafe { + funfloat((&a as *const i32).cast::() as *mut f64); + } + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test15: Addr-of a variable passed into an invoke instruction +// CHECK-LABEL: test15{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test15() -> i32 { + let mut a: i32 = 0; + + except(&mut a as *mut i32); + + 0 + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test16: Addr-of a struct element passed into an invoke instruction +// (GEP followed by an invoke) +// CHECK-LABEL: test16{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test16() -> i32 { + let mut c = Pair { a: 0, b: 0 }; + + except(&mut c.a as *mut i32); + + 0 + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test17: Addr-of a pointer +// CHECK-LABEL: test17{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test17() { + let mut a: *mut i32 = getp(); + + let mut _b: *mut *mut i32 = std::ptr::null_mut(); + + std::ptr::write_volatile(&mut _b, &mut a); + + let tmp = std::ptr::read_volatile(&_b); + + funcall2(tmp); + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test18: Addr-of a casted pointer +// CHECK-LABEL: test18{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test18() { + let mut a: *mut i32 = getp(); + + let mut _b: *mut *mut i32 = std::ptr::null_mut(); + + std::ptr::write_volatile(&mut _b, &mut a); + + let tmp = std::ptr::read_volatile(&_b); + + funfloat2(tmp as *mut *mut f64); + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test19: array of [4 x i32] +// CHECK-LABEL: test19{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test19() -> i32 { + let a: [i32; 4] = [0; 4]; + + let _whole = std::ptr::read_volatile(&a as *const _); // avoid array a from optimization + + std::ptr::read_volatile(&a[0]) + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// test20: Nested structure, no arrays, no address-of expressions +// Verify that the resulting gep-of-gep does not incorrectly trigger +// a stack protector +// CHECK-LABEL: test20{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test20() { + let c = Nest { first: Pair { a: 10, b: 11 }, second: Pair { a: 20, b: 21 } }; + + let whole: Nest = std::ptr::read_volatile(&c); + + let v: i32 = whole.second.a; + + printf(STR.as_ptr(), v); + + // strong-NOT: __stack_chk_fail +} + +// test21: Address-of a structure taken in a function with a loop where +// the alloca is an incoming value to a PHI node and a use of that PHI +// node is also an incoming value +// Verify that the address-of analysis does not get stuck in infinite +// recursion when chasing the alloca through the PHI nodes +// CHECK-LABEL: test21{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test21() -> i32 { + let mut tmp: *mut u8 = std::ptr::null_mut(); + let tmp_ptr: *mut *mut u8 = &mut tmp; + + let tmp1 = dummy(tmp_ptr); + + let cur = std::ptr::read_volatile(tmp_ptr); + + let v = (cur as usize as i64) as i32; + + if v > 0 { + let mut phi_ptr: *mut u8 = cur; + let mut phi_idx: i64 = 1; + let mut phi_acc: i32 = tmp1; + + loop { + let b = std::ptr::read_volatile(phi_ptr as *const u8); + let cond = b == 1u8; + let plus = phi_acc.wrapping_add(8); + let next_acc = if cond { plus } else { phi_acc }; + + if (phi_idx as i32) == v { + dummy(next_acc); + break; + } + + let slot = tmp_ptr.add(phi_idx as usize); + let next = std::ptr::read_volatile(slot); + phi_ptr = next; + phi_idx += 1; + phi_acc = next_acc; + } + } else { + dummy(tmp1); + } + + 0 + + // strong: __stack_chk_fail +} + +// CHECK-LABEL: IgnoreIntrinsicTest{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn IgnoreIntrinsicTest() -> i32 { + let mut x: i32 = 0; + + std::ptr::write_volatile(&mut x, 1); + + let y = std::ptr::read_volatile(&x); + + let result = y.wrapping_mul(42); + + result + + // strong-NOT: __stack_chk_fail +} diff --git a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-32bit-2.rs b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-32bit-2.rs new file mode 100644 index 0000000000000..8cd7f730bd5e5 --- /dev/null +++ b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-32bit-2.rs @@ -0,0 +1,484 @@ +//@ revisions: all strong none missing +//@ assembly-output: emit-asm +//@ only-windows +//@ only-msvc +//@ ignore-64bit 64-bit table based SEH has slightly different behaviors than classic SEH +//@ [all] compile-flags: -Z stack-protector=all +//@ [strong] compile-flags: -Z stack-protector=strong +//@ [none] compile-flags: -Z stack-protector=none +//@ compile-flags: -C opt-level=2 -Z merge-functions=disabled + +#![crate_type = "lib"] +#![allow(internal_features)] +#![feature(unsized_fn_params)] + +extern "C" { + fn strcpy(dest: *mut u8, src: *const u8) -> *mut u8; + fn printf(fmt: *const u8, ...) -> i32; + fn funcall(p: *mut i32); + fn funcall2(p: *mut *mut i32); + fn funfloat(p: *mut f64); + fn funfloat2(p: *mut *mut f64); + fn testi_aux() -> f64; + fn getp() -> *mut i32; + fn dummy(_: ...) -> i32; + + static STR: [u8; 1]; +} + +extern "C-unwind" { + fn except(p: *mut i32); +} + +#[repr(C)] +struct Pair { + a: i32, + b: i32, +} + +#[repr(C)] +struct Nest { + first: Pair, + second: Pair, +} + +// test1: array of [16 x i8] +// CHECK-LABEL: test1{{:|\[}} +#[no_mangle] +pub fn test1(a: *const u8) { + let mut buf: [u8; 16] = [0; 16]; + + unsafe { + strcpy(buf.as_mut_ptr(), a); + printf(STR.as_ptr(), buf.as_ptr()); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test2: array [4 x i8] +// CHECK-LABEL: test2{{:|\[}} +#[no_mangle] +pub fn test2(a: *const u8) { + let mut buf: [u8; 4] = [0; 4]; + + unsafe { + strcpy(buf.as_mut_ptr(), a); + printf(STR.as_ptr(), buf.as_ptr()); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test3: no arrays / no nested arrays +// CHECK-LABEL: test3{{:|\[}} +#[no_mangle] +pub fn test3(a: *const u8) { + unsafe { + printf(STR.as_ptr(), a); + } + + // all: __security_check_cookie + // strong-NOT: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test4: Address-of local taken (j = &a) +// CHECK-LABEL: test4{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test4() { + let mut a: i32 = 0; + + let mut j: *mut i32 = std::ptr::null_mut(); + + let tmp = std::ptr::read_volatile(&a); + let tmp2 = tmp.wrapping_add(1); + std::ptr::write_volatile(&mut a, tmp2); + + std::ptr::write_volatile(&mut j, &mut a); + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test5: PtrToInt Cast +// CHECK-LABEL: test5{{:|\[}} +#[no_mangle] +pub fn test5(a: i32) { + let ptr_val: usize = &a as *const i32 as usize; + + unsafe { + printf(STR.as_ptr(), ptr_val); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test6: Passing addr-of to function call +// CHECK-LABEL: test6{{:|\[}} +#[no_mangle] +pub fn test6(mut b: i32) { + unsafe { + funcall(&mut b as *mut i32); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test7: Addr-of in select instruction +// CHECK-LABEL: test7{{:|\[}} +#[no_mangle] +pub fn test7() { + let x: f64; + + unsafe { + let call = testi_aux(); + x = call; + + let y: *const f64 = if call > 0.0 { &x as *const f64 } else { std::ptr::null() }; + + printf(STR.as_ptr(), y); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test8: Addr-of in phi instruction +// CHECK-LABEL: test8{{:|\[}} +#[no_mangle] +pub fn test8() { + let mut _x: f64; + + unsafe { + let call = testi_aux(); + _x = call; + + let y: *const f64; + + if call > 3.14 { + let call1 = testi_aux(); + _x = call1; + y = std::ptr::null(); + } else { + if call > 1.0 { + y = &_x; + } else { + y = std::ptr::null(); + } + } + + printf(STR.as_ptr(), y); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test9: Addr-of struct element(GEP followed by store) +// CHECK-LABEL: test9{{:|\[}} +#[no_mangle] +pub fn test9() { + let mut c = Pair { a: 0, b: 0 }; + let b: *mut i32; + + unsafe { + let y: *mut i32 = &mut c.b; + + b = y; + + printf(STR.as_ptr(), b); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test10: Addr-of struct element, GEP followed by ptrtoint +// CHECK-LABEL: test10{{:|\[}} +#[no_mangle] +pub fn test10() { + let mut c = Pair { a: 0, b: 0 }; + + unsafe { + let y: *mut i32 = &mut c.b; + + let addr: i64 = y as i64; + + printf(STR.as_ptr(), addr); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test11: Addr-of struct element, GEP followed by callinst +// CHECK-LABEL: test11{{:|\[}} +#[no_mangle] +pub fn test11() { + let mut c = Pair { a: 0, b: 0 }; + + unsafe { + let y: *mut i32 = &mut c.b; + + printf(STR.as_ptr(), y); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test12: Addr-of a local, optimized into a GEP (e.g., &a - 12) +// CHECK-LABEL: test12{{:|\[}} +#[no_mangle] +pub fn test12() { + let mut a: i32 = 0; + + unsafe { + let add_ptr = (&mut a as *mut i32).offset(-12); + + printf(STR.as_ptr(), add_ptr); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test13: Addr-of a local cast to a ptr of a different type +// (e.g., int a; ... ; ptr b = &a;) +// CHECK-LABEL: test13{{:|\[}} +#[no_mangle] +pub fn test13() { + let mut a: i32 = 0; + + unsafe { + let mut b: *mut i32 = std::ptr::null_mut(); + + std::ptr::write_volatile(&mut b, &mut a); // avoid ptr b from optimization + let tmp = std::ptr::read_volatile(&b); + + printf(STR.as_ptr(), tmp); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test14: Addr-of a local cast to a ptr of a different type (optimized) +// (e.g., int a; ... ; ptr b = &a;) +// CHECK-LABEL: test14{{:|\[}} +#[no_mangle] +pub fn test14() { + let a: i32 = 0; + unsafe { + funfloat((&a as *const i32).cast::() as *mut f64); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test15: Addr-of a variable passed into an invoke instruction +// CHECK-LABEL: test15{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test15() -> i32 { + let mut a: i32 = 0; + + except(&mut a as *mut i32); + + 0 + + // stack protector does not generated by LLVM because of Windows SEH. + + // all-NOT: __security_check_cookie + // strong-NOT: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test16: Addr-of a struct element passed into an invoke instruction +// (GEP followed by an invoke) +// CHECK-LABEL: test16{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test16() -> i32 { + let mut c = Pair { a: 0, b: 0 }; + + except(&mut c.a as *mut i32); + + 0 + + // stack protector does not generated by LLVM because of Windows SEH. + + // all-NOT: __security_check_cookie + // strong-NOT: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test17: Addr-of a pointer +// CHECK-LABEL: test17{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test17() { + let mut a: *mut i32 = getp(); + + let mut _b: *mut *mut i32 = std::ptr::null_mut(); + + std::ptr::write_volatile(&mut _b, &mut a); + + let tmp = std::ptr::read_volatile(&_b); + + funcall2(tmp); + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test18: Addr-of a casted pointer +// CHECK-LABEL: test18{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test18() { + let mut a: *mut i32 = getp(); + + let mut _b: *mut *mut i32 = std::ptr::null_mut(); + + std::ptr::write_volatile(&mut _b, &mut a); + + let tmp = std::ptr::read_volatile(&_b); + + funfloat2(tmp as *mut *mut f64); + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test19: array of [4 x i32] +// CHECK-LABEL: test19{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test19() -> i32 { + let a: [i32; 4] = [0; 4]; + + let _whole = std::ptr::read_volatile(&a as *const _); // avoid array a from optimization + + std::ptr::read_volatile(&a[0]) + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test20: Nested structure, no arrays, no address-of expressions +// Verify that the resulting gep-of-gep does not incorrectly trigger +// a stack protector +// CHECK-LABEL: test20{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test20() { + let c = Nest { first: Pair { a: 10, b: 11 }, second: Pair { a: 20, b: 21 } }; + + let whole: Nest = std::ptr::read_volatile(&c); + + let v: i32 = whole.second.a; + + printf(STR.as_ptr(), v); + + // strong-NOT: __security_check_cookie +} + +// test21: Address-of a structure taken in a function with a loop where +// the alloca is an incoming value to a PHI node and a use of that PHI +// node is also an incoming value +// Verify that the address-of analysis does not get stuck in infinite +// recursion when chasing the alloca through the PHI nodes +// CHECK-LABEL: test21{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test21() -> i32 { + let mut tmp: *mut u8 = std::ptr::null_mut(); + let tmp_ptr: *mut *mut u8 = &mut tmp; + + let tmp1 = dummy(tmp_ptr); + + let cur = std::ptr::read_volatile(tmp_ptr); + + let v = (cur as usize as i64) as i32; + + if v > 0 { + let mut phi_ptr: *mut u8 = cur; + let mut phi_idx: i64 = 1; + let mut phi_acc: i32 = tmp1; + + loop { + let b = std::ptr::read_volatile(phi_ptr as *const u8); + let cond = b == 1u8; + let plus = phi_acc.wrapping_add(8); + let next_acc = if cond { plus } else { phi_acc }; + + if (phi_idx as i32) == v { + dummy(next_acc); + break; + } + + let slot = tmp_ptr.add(phi_idx as usize); + let next = std::ptr::read_volatile(slot); + phi_ptr = next; + phi_idx += 1; + phi_acc = next_acc; + } + } else { + dummy(tmp1); + } + + 0 + + // strong: __security_check_cookie +} + +// CHECK-LABEL: IgnoreIntrinsicTest{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn IgnoreIntrinsicTest() -> i32 { + let mut x: i32 = 0; + + std::ptr::write_volatile(&mut x, 1); + + let y = std::ptr::read_volatile(&x); + + let result = y.wrapping_mul(42); + + result + + // strong-NOT: __security_check_cookie +} diff --git a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit-2.rs b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit-2.rs new file mode 100644 index 0000000000000..ac0f89b8e9c0a --- /dev/null +++ b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit-2.rs @@ -0,0 +1,491 @@ +//@ revisions: all strong none missing +//@ assembly-output: emit-asm +//@ only-windows +//@ only-msvc +//@ ignore-32bit 64-bit table based SEH has slightly different behaviors than classic SEH +//@ [all] compile-flags: -Z stack-protector=all +//@ [strong] compile-flags: -Z stack-protector=strong +//@ [none] compile-flags: -Z stack-protector=none +//@ compile-flags: -C opt-level=2 -Z merge-functions=disabled + +#![crate_type = "lib"] +#![feature(unsized_fn_params)] + +extern "C" { + fn strcpy(dest: *mut u8, src: *const u8) -> *mut u8; + fn printf(fmt: *const u8, ...) -> i32; + fn funcall(p: *mut i32); + fn funcall2(p: *mut *mut i32); + fn funfloat(p: *mut f64); + fn funfloat2(p: *mut *mut f64); + fn testi_aux() -> f64; + fn getp() -> *mut i32; + fn dummy(_: ...) -> i32; + + static STR: [u8; 1]; +} + +extern "C-unwind" { + fn except(p: *mut i32); +} + +#[repr(C)] +struct Pair { + a: i32, + b: i32, +} + +#[repr(C)] +struct Nest { + first: Pair, + second: Pair, +} + +// test1: array of [16 x i8] +// CHECK-LABEL: test1{{:|\[}} +#[no_mangle] +pub fn test1(a: *const u8) { + let mut buf: [u8; 16] = [0; 16]; + + unsafe { + strcpy(buf.as_mut_ptr(), a); + printf(STR.as_ptr(), buf.as_ptr()); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test2: array [4 x i8] +// CHECK-LABEL: test2{{:|\[}} +#[no_mangle] +pub fn test2(a: *const u8) { + let mut buf: [u8; 4] = [0; 4]; + + unsafe { + strcpy(buf.as_mut_ptr(), a); + printf(STR.as_ptr(), buf.as_ptr()); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test3: no arrays / no nested arrays +// CHECK-LABEL: test3{{:|\[}} +#[no_mangle] +pub fn test3(a: *const u8) { + unsafe { + printf(STR.as_ptr(), a); + } + + // all: __security_check_cookie + // strong-NOT: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test4: Address-of local taken (j = &a) +// CHECK-LABEL: test4{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test4() { + let mut a: i32 = 0; + + let mut j: *mut i32 = std::ptr::null_mut(); + + let tmp = std::ptr::read_volatile(&a); + let tmp2 = tmp.wrapping_add(1); + std::ptr::write_volatile(&mut a, tmp2); + + std::ptr::write_volatile(&mut j, &mut a); + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test5: PtrToInt Cast +// CHECK-LABEL: test5{{:|\[}} +#[no_mangle] +pub fn test5(a: i32) { + let ptr_val: usize = &a as *const i32 as usize; + + unsafe { + printf(STR.as_ptr(), ptr_val); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test6: Passing addr-of to function call +// CHECK-LABEL: test6{{:|\[}} +#[no_mangle] +pub fn test6(mut b: i32) { + unsafe { + funcall(&mut b as *mut i32); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test7: Addr-of in select instruction +// CHECK-LABEL: test7{{:|\[}} +#[no_mangle] +pub fn test7() { + let x: f64; + + unsafe { + let call = testi_aux(); + x = call; + + let y: *const f64 = if call > 0.0 { &x as *const f64 } else { std::ptr::null() }; + + printf(STR.as_ptr(), y); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test8: Addr-of in phi instruction +// CHECK-LABEL: test8{{:|\[}} +#[no_mangle] +pub fn test8() { + let mut _x: f64; + + unsafe { + let call = testi_aux(); + _x = call; + + let y: *const f64; + + if call > 3.14 { + let call1 = testi_aux(); + _x = call1; + y = std::ptr::null(); + } else { + if call > 1.0 { + y = &_x; + } else { + y = std::ptr::null(); + } + } + + printf(STR.as_ptr(), y); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test9: Addr-of struct element(GEP followed by store) +// CHECK-LABEL: test9{{:|\[}} +#[no_mangle] +pub fn test9() { + let mut c = Pair { a: 0, b: 0 }; + let b: *mut i32; + + unsafe { + let y: *mut i32 = &mut c.b; + + b = y; + + printf(STR.as_ptr(), b); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test10: Addr-of struct element, GEP followed by ptrtoint +// CHECK-LABEL: test10{{:|\[}} +#[no_mangle] +pub fn test10() { + let mut c = Pair { a: 0, b: 0 }; + + unsafe { + let y: *mut i32 = &mut c.b; + + let addr: i64 = y as i64; + + printf(STR.as_ptr(), addr); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test11: Addr-of struct element, GEP followed by callinst +// CHECK-LABEL: test11{{:|\[}} +#[no_mangle] +pub fn test11() { + let mut c = Pair { a: 0, b: 0 }; + + unsafe { + let y: *mut i32 = &mut c.b; + + printf(STR.as_ptr(), y); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test12: Addr-of a local, optimized into a GEP (e.g., &a - 12) +// CHECK-LABEL: test12{{:|\[}} +#[no_mangle] +pub fn test12() { + let mut a: i32 = 0; + + unsafe { + let add_ptr = (&mut a as *mut i32).offset(-12); + + printf(STR.as_ptr(), add_ptr); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test13: Addr-of a local cast to a ptr of a different type +// (e.g., int a; ... ; ptr b = &a;) +// CHECK-LABEL: test13{{:|\[}} +#[no_mangle] +pub fn test13() { + let mut a: i32 = 0; + + unsafe { + let mut b: *mut i32 = std::ptr::null_mut(); + + std::ptr::write_volatile(&mut b, &mut a); // avoid ptr b from optimization + let tmp = std::ptr::read_volatile(&b); + + printf(STR.as_ptr(), tmp); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test14: Addr-of a local cast to a ptr of a different type (optimized) +// (e.g., int a; ... ; ptr b = &a;) +// CHECK-LABEL: test14{{:|\[}} +#[no_mangle] +pub fn test14() { + let a: i32 = 0; + unsafe { + funfloat((&a as *const i32).cast::() as *mut f64); + } + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test15: Addr-of a variable passed into an invoke instruction +// CHECK-LABEL: test15{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test15() -> i32 { + // CHECK-DAG: .seh_endprologue + + let mut a: i32 = 0; + + except(&mut a as *mut i32); + + 0 + + // stack protector does not generated by LLVM because of Windows SEH. + + // all-NOT: __security_check_cookie + // strong-NOT: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc +} + +// test16: Addr-of a struct element passed into an invoke instruction +// (GEP followed by an invoke) +// CHECK-LABEL: test16{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test16() -> i32 { + // CHECK-DAG: .seh_endprologue + + let mut c = Pair { a: 0, b: 0 }; + + except(&mut c.a as *mut i32); + + 0 + + // stack protector does not generated by LLVM because of Windows SEH. + + // all-NOT: __security_check_cookie + // strong-NOT: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc +} + +// test17: Addr-of a pointer +// CHECK-LABEL: test17{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test17() { + let mut a: *mut i32 = getp(); + + let mut _b: *mut *mut i32 = std::ptr::null_mut(); + + std::ptr::write_volatile(&mut _b, &mut a); + + let tmp = std::ptr::read_volatile(&_b); + + funcall2(tmp); + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test18: Addr-of a casted pointer +// CHECK-LABEL: test18{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test18() { + let mut a: *mut i32 = getp(); + + let mut _b: *mut *mut i32 = std::ptr::null_mut(); + + std::ptr::write_volatile(&mut _b, &mut a); + + let tmp = std::ptr::read_volatile(&_b); + + funfloat2(tmp as *mut *mut f64); + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test19: array of [4 x i32] +// CHECK-LABEL: test19{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test19() -> i32 { + let a: [i32; 4] = [0; 4]; + + let _whole = std::ptr::read_volatile(&a as *const _); // avoid array a from optimization + + std::ptr::read_volatile(&a[0]) + + // all: __security_check_cookie + // strong: __security_check_cookie + // none-NOT: __security_check_cookie + // missing-NOT: __security_check_cookie +} + +// test20: Nested structure, no arrays, no address-of expressions +// Verify that the resulting gep-of-gep does not incorrectly trigger +// a stack protector +// CHECK-LABEL: test20{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test20() { + let c = Nest { first: Pair { a: 10, b: 11 }, second: Pair { a: 20, b: 21 } }; + + let whole: Nest = std::ptr::read_volatile(&c); + + let v: i32 = whole.second.a; + + printf(STR.as_ptr(), v); + + // strong-NOT: __security_check_cookie +} + +// test21: Address-of a structure taken in a function with a loop where +// the alloca is an incoming value to a PHI node and a use of that PHI +// node is also an incoming value +// Verify that the address-of analysis does not get stuck in infinite +// recursion when chasing the alloca through the PHI nodes +// CHECK-LABEL: test21{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn test21() -> i32 { + let mut tmp: *mut u8 = std::ptr::null_mut(); + let tmp_ptr: *mut *mut u8 = &mut tmp; + + let tmp1 = dummy(tmp_ptr); + + let cur = std::ptr::read_volatile(tmp_ptr); + + let v = (cur as usize as i64) as i32; + + if v > 0 { + let mut phi_ptr: *mut u8 = cur; + let mut phi_idx: i64 = 1; + let mut phi_acc: i32 = tmp1; + + loop { + let b = std::ptr::read_volatile(phi_ptr as *const u8); + let cond = b == 1u8; + let plus = phi_acc.wrapping_add(8); + let next_acc = if cond { plus } else { phi_acc }; + + if (phi_idx as i32) == v { + dummy(next_acc); + break; + } + + let slot = tmp_ptr.add(phi_idx as usize); + let next = std::ptr::read_volatile(slot); + phi_ptr = next; + phi_idx += 1; + phi_acc = next_acc; + } + } else { + dummy(tmp1); + } + + 0 + + // strong: __security_check_cookie +} + +// CHECK-LABEL: IgnoreIntrinsicTest{{:|\[}} +#[no_mangle] +pub unsafe extern "C" fn IgnoreIntrinsicTest() -> i32 { + let mut x: i32 = 0; + + std::ptr::write_volatile(&mut x, 1); + + let y = std::ptr::read_volatile(&x); + + let result = y.wrapping_mul(42); + + result + + // strong-NOT: __security_check_cookie +} diff --git a/tests/codegen-llvm/char-ascii-branchless.rs b/tests/codegen-llvm/char-ascii-branchless.rs index f99066aa9aaec..2cd5b5135a407 100644 --- a/tests/codegen-llvm/char-ascii-branchless.rs +++ b/tests/codegen-llvm/char-ascii-branchless.rs @@ -7,41 +7,41 @@ // CHECK-LABEL: @is_ascii_alphanumeric_char #[no_mangle] pub fn is_ascii_alphanumeric_char(x: char) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_alphanumeric() } // CHECK-LABEL: @is_ascii_alphanumeric_u8 #[no_mangle] pub fn is_ascii_alphanumeric_u8(x: u8) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_alphanumeric() } // CHECK-LABEL: @is_ascii_hexdigit_char #[no_mangle] pub fn is_ascii_hexdigit_char(x: char) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_hexdigit() } // CHECK-LABEL: @is_ascii_hexdigit_u8 #[no_mangle] pub fn is_ascii_hexdigit_u8(x: u8) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_hexdigit() } // CHECK-LABEL: @is_ascii_punctuation_char #[no_mangle] pub fn is_ascii_punctuation_char(x: char) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_punctuation() } // CHECK-LABEL: @is_ascii_punctuation_u8 #[no_mangle] pub fn is_ascii_punctuation_u8(x: u8) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.is_ascii_punctuation() } diff --git a/tests/codegen-llvm/enum/enum-early-otherwise-branch.rs b/tests/codegen-llvm/enum/enum-early-otherwise-branch.rs index 8d39d8e9b7466..4cadef3b6988a 100644 --- a/tests/codegen-llvm/enum/enum-early-otherwise-branch.rs +++ b/tests/codegen-llvm/enum/enum-early-otherwise-branch.rs @@ -12,7 +12,7 @@ pub enum Enum { pub fn foo(lhs: &Enum, rhs: &Enum) -> bool { // CHECK-LABEL: define{{.*}}i1 @foo( // CHECK-NOT: switch - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: [[SELECT:%.*]] = select // CHECK-NEXT: ret i1 [[SELECT]] // CHECK-NEXT: } diff --git a/tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs b/tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs index b97729fa14657..c143f7aeabfa4 100644 --- a/tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs +++ b/tests/codegen-llvm/issues/cows-dont-have-branches-117763.rs @@ -9,7 +9,7 @@ // CHECK-LABEL: @branchless_cow_slices #[no_mangle] pub fn branchless_cow_slices<'a>(cow: &'a std::borrow::Cow<'a, [u8]>) -> &'a [u8] { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK-NOT: icmp // CHECK: ret { ptr, {{i32|i64}} } diff --git a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs index 69aefc6b1fb54..b8b9ea7436f33 100644 --- a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs +++ b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs @@ -11,7 +11,7 @@ use std::slice::Iter; #[no_mangle] pub unsafe fn foo(x: &mut Copied>) -> u32 { // CHECK-LABEL: @foo( - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK: [[RET:%.*]] = load i32, ptr // CHECK-NEXT: ret i32 [[RET]] diff --git a/tests/codegen-llvm/issues/issue-108395-branchy-bool-match.rs b/tests/codegen-llvm/issues/issue-108395-branchy-bool-match.rs index 96387e791b03a..86bbbadcd9062 100644 --- a/tests/codegen-llvm/issues/issue-108395-branchy-bool-match.rs +++ b/tests/codegen-llvm/issues/issue-108395-branchy-bool-match.rs @@ -6,7 +6,7 @@ // CHECK-LABEL: @wildcard( #[no_mangle] pub fn wildcard(a: u16, b: u16, v: u16) -> u16 { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} match (a == v, b == v) { (true, false) => 0, (false, true) => u16::MAX, @@ -17,7 +17,7 @@ pub fn wildcard(a: u16, b: u16, v: u16) -> u16 { // CHECK-LABEL: @exhaustive( #[no_mangle] pub fn exhaustive(a: u16, b: u16, v: u16) -> u16 { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} match (a == v, b == v) { (true, false) => 0, (false, true) => u16::MAX, diff --git a/tests/codegen-llvm/issues/issue-119422.rs b/tests/codegen-llvm/issues/issue-119422.rs index 17ae71605b58d..2c553fd1fa876 100644 --- a/tests/codegen-llvm/issues/issue-119422.rs +++ b/tests/codegen-llvm/issues/issue-119422.rs @@ -19,45 +19,45 @@ pub fn check_non_null(x: NonNull) -> bool { // CHECK-LABEL: @equals_zero_is_false_u8 #[no_mangle] pub fn equals_zero_is_false_u8(x: NonZero) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i1 false - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.get() == 0 } // CHECK-LABEL: @not_equals_zero_is_true_u8 #[no_mangle] pub fn not_equals_zero_is_true_u8(x: NonZero) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i1 true - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.get() != 0 } // CHECK-LABEL: @equals_zero_is_false_i8 #[no_mangle] pub fn equals_zero_is_false_i8(x: NonZero) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i1 false - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.get() == 0 } // CHECK-LABEL: @not_equals_zero_is_true_i8 #[no_mangle] pub fn not_equals_zero_is_true_i8(x: NonZero) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i1 true - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} x.get() != 0 } // CHECK-LABEL: @usize_try_from_u32 #[no_mangle] pub fn usize_try_from_u32(x: NonZero) -> NonZero { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: zext i32 %{{.*}} to i64 - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i64 x.try_into().unwrap() } @@ -65,9 +65,9 @@ pub fn usize_try_from_u32(x: NonZero) -> NonZero { // CHECK-LABEL: @isize_try_from_i32 #[no_mangle] pub fn isize_try_from_i32(x: NonZero) -> NonZero { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: sext i32 %{{.*}} to i64 - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i64 x.try_into().unwrap() } @@ -75,9 +75,9 @@ pub fn isize_try_from_i32(x: NonZero) -> NonZero { // CHECK-LABEL: @u64_from_nonzero_is_not_zero #[no_mangle] pub fn u64_from_nonzero_is_not_zero(x: NonZero) -> bool { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret i1 false - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} let v: u64 = x.into(); v == 0 } diff --git a/tests/codegen-llvm/issues/issue-73258.rs b/tests/codegen-llvm/issues/issue-73258.rs index 936a75544966b..c9eceb0dccf73 100644 --- a/tests/codegen-llvm/issues/issue-73258.rs +++ b/tests/codegen-llvm/issues/issue-73258.rs @@ -18,7 +18,7 @@ pub enum Foo { pub unsafe fn issue_73258(ptr: *const Foo) -> Foo { // CHECK-NOT: icmp // CHECK-NOT: call - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK: %[[R:.+]] = load i8 @@ -26,14 +26,14 @@ pub unsafe fn issue_73258(ptr: *const Foo) -> Foo { // CHECK-NOT: icmp // CHECK-NOT: call - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK: ret i8 %[[R]] // CHECK-NOT: icmp // CHECK-NOT: call - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: select let k: Option = Some(ptr.read()); return k.unwrap(); diff --git a/tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs b/tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs index 1d23eb2cdf9e4..4f5eea5f7e1c4 100644 --- a/tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs +++ b/tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs @@ -8,7 +8,7 @@ // CHECK-LABEL: @num_to_digit_slow #[no_mangle] pub fn num_to_digit_slow(num: char) -> u32 { - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: panic if num.is_digit(8) { num.to_digit(8).unwrap() } else { 0 } } diff --git a/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs b/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs index 6f34097842800..1da7de535f75c 100644 --- a/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs +++ b/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs @@ -19,23 +19,23 @@ impl Drop for NotCopy { // CHECK-LABEL: @iter_repeat_n_next pub fn iter_repeat_n_next(it: &mut std::iter::RepeatN) -> Option { // CHECK-NEXT: start: - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: %[[COUNT:.+]] = load i64 // CHECK-NEXT: %[[COUNT_ZERO:.+]] = icmp eq i64 %[[COUNT]], 0 // CHECK-NEXT: br i1 %[[COUNT_ZERO]], label %[[EMPTY:.+]], label %[[NOT_EMPTY:.+]] // CHECK: [[NOT_EMPTY]]: - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: %[[DEC:.+]] = add i64 %[[COUNT]], -1 // CHECK-NEXT: %[[VAL:.+]] = load i16 // CHECK-NEXT: store i64 %[[DEC]] // CHECK-NEXT: br label %[[EMPTY]] // CHECK: [[EMPTY]]: - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: phi i16 // CHECK-SAME: [ %[[VAL]], %[[NOT_EMPTY]] ] - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK: ret it.next() diff --git a/tests/codegen-llvm/option-as-slice.rs b/tests/codegen-llvm/option-as-slice.rs index 39b34a2035b74..79cdd613aaadc 100644 --- a/tests/codegen-llvm/option-as-slice.rs +++ b/tests/codegen-llvm/option-as-slice.rs @@ -11,14 +11,14 @@ use core::option::Option; #[no_mangle] pub fn u64_opt_as_slice(o: &Option) -> &[u64] { // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[LEN:.+]] = load i64 // CHECK-SAME: !range ![[META_U64:[0-9]+]], // CHECK-SAME: !noundef // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %{{.+}}, 0 @@ -31,13 +31,13 @@ pub fn u64_opt_as_slice(o: &Option) -> &[u64] { #[no_mangle] pub fn nonzero_u64_opt_as_slice(o: &Option>) -> &[NonZero] { // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[NZ:.+]] = icmp ne i64 %{{.+}}, 0 // CHECK-NEXT: %[[LEN:.+]] = zext i1 %[[NZ]] to i64 // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %o, 0 @@ -50,7 +50,7 @@ pub fn nonzero_u64_opt_as_slice(o: &Option>) -> &[NonZero] { #[no_mangle] pub fn u8_opt_as_slice(o: &Option) -> &[u8] { // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[TAG:.+]] = load i8 @@ -58,7 +58,7 @@ pub fn u8_opt_as_slice(o: &Option) -> &[u8] { // CHECK-SAME: !noundef // CHECK: %[[LEN:.+]] = zext{{.*}} i8 %[[TAG]] to i64 // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %{{.+}}, 0 diff --git a/tests/codegen-llvm/slice-init.rs b/tests/codegen-llvm/slice-init.rs index 950e0b0c10db7..12e9df24565e1 100644 --- a/tests/codegen-llvm/slice-init.rs +++ b/tests/codegen-llvm/slice-init.rs @@ -69,7 +69,7 @@ const N: usize = 100; #[no_mangle] pub fn u16_init_one_bytes() -> [u16; N] { // CHECK-NOT: select - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: switch // CHECK-NOT: icmp // CHECK: call void @llvm.memset.p0 diff --git a/tests/codegen-llvm/slice-iter-fold.rs b/tests/codegen-llvm/slice-iter-fold.rs index 55ab34661c36e..c63b5f8f7acdd 100644 --- a/tests/codegen-llvm/slice-iter-fold.rs +++ b/tests/codegen-llvm/slice-iter-fold.rs @@ -5,7 +5,7 @@ #[no_mangle] pub fn slice_fold_to_last(slice: &[i32]) -> Option<&i32> { // CHECK-NOT: loop - // CHECK-NOT: br + // CHECK-NOT: br {{.*}} // CHECK-NOT: call // CHECK: ret slice.iter().fold(None, |_, i| Some(i)) diff --git a/tests/codegen-llvm/vecdeque-drain.rs b/tests/codegen-llvm/vecdeque-drain.rs index a5e5da6501332..5709ead8bf939 100644 --- a/tests/codegen-llvm/vecdeque-drain.rs +++ b/tests/codegen-llvm/vecdeque-drain.rs @@ -10,7 +10,7 @@ use std::collections::VecDeque; // CHECK-LABEL: @clear // CHECK-NOT: call -// CHECK-NOT: br +// CHECK-NOT: br {{.*}} // CHECK: getelementptr inbounds // CHECK-NEXT: {{call void @llvm.memset|store}} // CHECK-NEXT: ret void @@ -21,15 +21,15 @@ pub fn clear(v: &mut VecDeque) { // CHECK-LABEL: @truncate // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK-NOT: br +// CHECK-NOT: br {{.*}} // CHECK: ret void #[no_mangle] pub fn truncate(v: &mut VecDeque, n: usize) { @@ -40,17 +40,17 @@ pub fn truncate(v: &mut VecDeque, n: usize) { // CHECK-LABEL: @advance // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK: br +// CHECK: br {{.*}} // CHECK-NOT: call -// CHECK-NOT: br +// CHECK-NOT: br {{.*}} // CHECK: ret void #[no_mangle] pub fn advance(v: &mut VecDeque, n: usize) { diff --git a/tests/rustdoc-js/alias-sort.js b/tests/rustdoc-js/alias-sort.js new file mode 100644 index 0000000000000..245ff416643c7 --- /dev/null +++ b/tests/rustdoc-js/alias-sort.js @@ -0,0 +1,10 @@ +// rank doc aliases lower than exact matches +// regression test for + +const EXPECTED = { + 'query': 'foobazbar', + 'others': [ + { 'name': 'foobazbar' }, + { 'name': 'foo' }, + ], +}; diff --git a/tests/rustdoc-js/alias-sort.rs b/tests/rustdoc-js/alias-sort.rs new file mode 100644 index 0000000000000..542bb4be4387e --- /dev/null +++ b/tests/rustdoc-js/alias-sort.rs @@ -0,0 +1,5 @@ +/// asdf +pub fn foobazbar() {} + +#[doc(alias("foobazbar"))] +pub fn foo() {} diff --git a/tests/ui/consts/const-eval/const_raw_ptr_ops.gated.stderr b/tests/ui/consts/const-eval/const_raw_ptr_ops.gated.stderr new file mode 100644 index 0000000000000..c39048c8f283c --- /dev/null +++ b/tests/ui/consts/const-eval/const_raw_ptr_ops.gated.stderr @@ -0,0 +1,23 @@ +error[E0277]: pointers cannot be reliably compared during const eval + --> $DIR/const_raw_ptr_ops.rs:7:26 + | +LL | const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: trait `PartialEq` is implemented but not `const` + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + = note: see issue #53020 for more information + +error[E0277]: pointers cannot be reliably compared during const eval + --> $DIR/const_raw_ptr_ops.rs:9:27 + | +LL | const X2: bool = unsafe { 42 as *const i32 == 43 as *const i32 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: trait `PartialEq` is implemented but not `const` + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + = note: see issue #53020 for more information + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/const-eval/const_raw_ptr_ops.rs b/tests/ui/consts/const-eval/const_raw_ptr_ops.rs index 432a05756d305..c26ef164ebab9 100644 --- a/tests/ui/consts/const-eval/const_raw_ptr_ops.rs +++ b/tests/ui/consts/const-eval/const_raw_ptr_ops.rs @@ -1,3 +1,6 @@ +//@revisions: stable gated +#![cfg_attr(gated, feature(const_trait_impl))] + fn main() {} // unconst and bad, will thus error in miri diff --git a/tests/ui/consts/const-eval/const_raw_ptr_ops.stderr b/tests/ui/consts/const-eval/const_raw_ptr_ops.stable.stderr similarity index 89% rename from tests/ui/consts/const-eval/const_raw_ptr_ops.stderr rename to tests/ui/consts/const-eval/const_raw_ptr_ops.stable.stderr index 1f5bca273d3b0..2c7e6e8671351 100644 --- a/tests/ui/consts/const-eval/const_raw_ptr_ops.stderr +++ b/tests/ui/consts/const-eval/const_raw_ptr_ops.stable.stderr @@ -1,5 +1,5 @@ error: pointers cannot be reliably compared during const eval - --> $DIR/const_raw_ptr_ops.rs:4:26 + --> $DIR/const_raw_ptr_ops.rs:7:26 | LL | const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; = note: see issue #53020 for more information error: pointers cannot be reliably compared during const eval - --> $DIR/const_raw_ptr_ops.rs:6:27 + --> $DIR/const_raw_ptr_ops.rs:9:27 | LL | const X2: bool = unsafe { 42 as *const i32 == 43 as *const i32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/different-fn-ptr-binders-during-ctfe.gated.stderr b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.gated.stderr new file mode 100644 index 0000000000000..f13e0c6661814 --- /dev/null +++ b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.gated.stderr @@ -0,0 +1,13 @@ +error[E0277]: pointers cannot be reliably compared during const eval + --> $DIR/different-fn-ptr-binders-during-ctfe.rs:5:5 + | +LL | x == y + | ^^^^^^ + | +note: trait `PartialEq` is implemented but not `const` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + = note: see issue #53020 for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/different-fn-ptr-binders-during-ctfe.rs b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.rs index b378542e5730c..7f963eabd6d56 100644 --- a/tests/ui/consts/different-fn-ptr-binders-during-ctfe.rs +++ b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.rs @@ -1,3 +1,6 @@ +//@revisions: stable gated +#![cfg_attr(gated, feature(const_trait_impl))] + const fn cmp(x: fn(&'static ()), y: for<'a> fn(&'a ())) -> bool { x == y //~^ ERROR pointers cannot be reliably compared during const eval diff --git a/tests/ui/consts/different-fn-ptr-binders-during-ctfe.stderr b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.stable.stderr similarity index 81% rename from tests/ui/consts/different-fn-ptr-binders-during-ctfe.stderr rename to tests/ui/consts/different-fn-ptr-binders-during-ctfe.stable.stderr index 43a7b9ce66ce0..92b09e7db0d45 100644 --- a/tests/ui/consts/different-fn-ptr-binders-during-ctfe.stderr +++ b/tests/ui/consts/different-fn-ptr-binders-during-ctfe.stable.stderr @@ -1,5 +1,5 @@ error: pointers cannot be reliably compared during const eval - --> $DIR/different-fn-ptr-binders-during-ctfe.rs:2:5 + --> $DIR/different-fn-ptr-binders-during-ctfe.rs:5:5 | LL | x == y | ^^^^^^ diff --git a/tests/ui/consts/issue-94675.stderr b/tests/ui/consts/issue-94675.stderr index d7664de5c07a7..771f2a14c3053 100644 --- a/tests/ui/consts/issue-94675.stderr +++ b/tests/ui/consts/issue-94675.stderr @@ -3,12 +3,18 @@ error[E0277]: the trait bound `Vec: [const] Index<_>` is not satisfied | LL | self.bar[0] = baz.len(); | ^^^^^^^^^^^ + | +note: trait `Index` is implemented but not `const` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL error[E0277]: the trait bound `Vec: [const] IndexMut` is not satisfied --> $DIR/issue-94675.rs:11:9 | LL | self.bar[0] = baz.len(); | ^^^^^^^^^^^ + | +note: trait `IndexMut` is implemented but not `const` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL error[E0277]: the trait bound `Vec: [const] Index` is not satisfied --> $DIR/issue-94675.rs:11:9 @@ -16,6 +22,8 @@ error[E0277]: the trait bound `Vec: [const] Index` is not satisfie LL | self.bar[0] = baz.len(); | ^^^^^^^^^^^ | +note: trait `Index` is implemented but not `const` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL note: required by a bound in `std::ops::IndexMut::index_mut` --> $SRC_DIR/core/src/ops/index.rs:LL:COL diff --git a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs new file mode 100644 index 0000000000000..c0af549e2d012 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs @@ -0,0 +1,32 @@ +#![feature(const_trait_impl, const_cmp)] +#![deny(misplaced_diagnostic_attributes)] + +#[diagnostic::on_const(message = "tadaa", note = "boing")] +//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls +pub struct Foo; + +#[diagnostic::on_const(message = "tadaa", note = "boing")] +//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls +impl const PartialEq for Foo { + fn eq(&self, _other: &Foo) -> bool { + true + } +} + +#[diagnostic::on_const(message = "tadaa", note = "boing")] +//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls +impl Foo { + fn eq(&self, _other: &Foo) -> bool { + true + } +} + +impl PartialOrd for Foo { + #[diagnostic::on_const(message = "tadaa", note = "boing")] + //~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls + fn partial_cmp(&self, other: &Foo) -> Option { + None + } +} + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr new file mode 100644 index 0000000000000..a12c00c74d055 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr @@ -0,0 +1,32 @@ +error: `#[diagnostic::on_const]` can only be applied to trait impls + --> $DIR/misplaced_attr.rs:4:1 + | +LL | #[diagnostic::on_const(message = "tadaa", note = "boing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/misplaced_attr.rs:2:9 + | +LL | #![deny(misplaced_diagnostic_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[diagnostic::on_const]` can only be applied to trait impls + --> $DIR/misplaced_attr.rs:8:1 + | +LL | #[diagnostic::on_const(message = "tadaa", note = "boing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[diagnostic::on_const]` can only be applied to trait impls + --> $DIR/misplaced_attr.rs:16:1 + | +LL | #[diagnostic::on_const(message = "tadaa", note = "boing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[diagnostic::on_const]` can only be applied to trait impls + --> $DIR/misplaced_attr.rs:25:5 + | +LL | #[diagnostic::on_const(message = "tadaa", note = "boing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/feature-gates/auxiliary/diagnostic-on-const.rs b/tests/ui/feature-gates/auxiliary/diagnostic-on-const.rs new file mode 100644 index 0000000000000..3d2b5b9a9239f --- /dev/null +++ b/tests/ui/feature-gates/auxiliary/diagnostic-on-const.rs @@ -0,0 +1,8 @@ +pub struct Foo; + +#[diagnostic::on_const(message = "tadaa", note = "boing")] +impl PartialEq for Foo { + fn eq(&self, _other: &Foo) -> bool { + true + } +} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-const.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-const.rs new file mode 100644 index 0000000000000..398fa30e74047 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-const.rs @@ -0,0 +1,16 @@ +//! This is an unusual feature gate test, as it doesn't test the feature +//! gate, but the fact that not adding the feature gate to +//! the aux crate will cause the diagnostic to not emit the +//! custom diagnostic message + +//@ aux-build: diagnostic-on-const.rs + +extern crate diagnostic_on_const; +use diagnostic_on_const::Foo; + +const fn foo() { + Foo == Foo; + //~^ ERROR: cannot call non-const operator in constant functions +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-const.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-const.stderr new file mode 100644 index 0000000000000..04c901f4f938b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-const.stderr @@ -0,0 +1,16 @@ +error[E0015]: cannot call non-const operator in constant functions + --> $DIR/feature-gate-diagnostic-on-const.rs:12:5 + | +LL | Foo == Foo; + | ^^^^^^^^^^ + | +note: impl defined here, but it is not `const` + --> $DIR/auxiliary/diagnostic-on-const.rs:4:1 + | +LL | impl PartialEq for Foo { + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/traits/const-traits/call-generic-method-fail.stderr b/tests/ui/traits/const-traits/call-generic-method-fail.stderr index 6e75730b9b513..a2fba141f7b89 100644 --- a/tests/ui/traits/const-traits/call-generic-method-fail.stderr +++ b/tests/ui/traits/const-traits/call-generic-method-fail.stderr @@ -3,11 +3,6 @@ error[E0277]: the trait bound `T: [const] PartialEq` is not satisfied | LL | *t == *t | ^^^^^^^^ - | -note: trait `PartialEq` is implemented but not `const` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: trait `PartialEq` is implemented but not `const` - --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL error: aborting due to 1 previous error