diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index fd0ef8500c6c3..d8d749cd35c4b 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -10,10 +10,10 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::errors::{ - EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, - EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition, - EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, - EiiStaticMultipleImplementations, EiiStaticMutable, + EiiAttributeNotSupported, EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, + EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, + EiiSharedMacroInStatementPosition, EiiSharedMacroTarget, EiiStaticArgumentRequired, + EiiStaticDefault, EiiStaticMultipleImplementations, EiiStaticMutable, }; /// ```rust @@ -128,6 +128,8 @@ fn eii_( let attrs_from_decl = filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path); + let (macro_attrs, foreign_item_attrs, default_func_attrs) = + split_attrs(ecx, item_span, attrs_from_decl); let Ok(macro_name) = name_for_impl_macro(ecx, foreign_item_name, &meta_item) else { // we don't need to wrap in Annotatable::Stmt conditionally since @@ -148,6 +150,7 @@ fn eii_( eii_attr_span, item_span, foreign_item_name, + default_func_attrs, )) } @@ -157,7 +160,7 @@ fn eii_( item_span, kind, vis, - &attrs_from_decl, + foreign_item_attrs, )); module_items.push(generate_attribute_macro_to_implement( ecx, @@ -165,7 +168,7 @@ fn eii_( macro_name, foreign_item_name, impl_unsafe, - &attrs_from_decl, + macro_attrs, )); // we don't need to wrap in Annotatable::Stmt conditionally since @@ -173,6 +176,49 @@ fn eii_( module_items.into_iter().map(Annotatable::Item).collect() } +fn split_attrs( + ecx: &mut ExtCtxt<'_>, + span: Span, + attrs: ThinVec, +) -> (ThinVec, ThinVec, ThinVec) { + let mut macro_attributes = ThinVec::new(); + let mut foreign_item_attributes = ThinVec::new(); + let mut default_attributes = ThinVec::new(); + + for attr in attrs { + match attr.name() { + // Inline only matters for the default function being inlined into callsites + Some(sym::inline) => default_attributes.push(attr), + // If an eii is marked a lang item, that's because we want to call its declaration, so + // mark the foreign item as the lang item + Some(sym::lang) => foreign_item_attributes.push(attr), + // Deprecating an eii means deprecating the macro and the foreign item + Some(sym::deprecated) => { + foreign_item_attributes.push(attr.clone()); + macro_attributes.push(attr); + } + // The stability of an EII affects the usage of the macro and calling the foreign item + Some(sym::stable) | Some(sym::unstable) => { + foreign_item_attributes.push(attr.clone()); + macro_attributes.push(attr); + } + // Doc attributes should be forwarded to the macro and the foreign item, since those are + // the two items you interact with as a user. + // FIXME: idk yet how EIIs show up in docs, might want to customize + _ if attr.is_doc_comment() => { + foreign_item_attributes.push(attr.clone()); + macro_attributes.push(attr); + } + Some(sym::eii) => unreachable!("should already be filtered out"), + _ => { + ecx.dcx().emit_err(EiiAttributeNotSupported { span, attr_span: attr.span() }); + } + } + } + + (macro_attributes, foreign_item_attributes, default_attributes) +} + /// Decide on the name of the macro that can be used to implement the EII. /// This is either an explicitly given name, or the name of the item in the /// declaration of the EII. @@ -228,10 +274,8 @@ fn generate_default_func_impl( eii_attr_span: Span, item_span: Span, foreign_item_name: Ident, + attrs: ThinVec, ) -> Box { - // FIXME: re-add some original attrs - let attrs = ThinVec::new(); - let mut default_func = func.clone(); default_func.eii_impls.push(EiiImpl { node_id: DUMMY_NODE_ID, @@ -289,10 +333,9 @@ fn generate_foreign_item( item_span: Span, item_kind: &ItemKind, vis: Visibility, - attrs_from_decl: &[Attribute], + attrs_from_decl: ThinVec, ) -> Box { - let mut foreign_item_attrs = ThinVec::new(); - foreign_item_attrs.extend_from_slice(attrs_from_decl); + let mut foreign_item_attrs = attrs_from_decl; // Add the rustc_eii_foreign_item on the foreign item. Usually, foreign items are mangled. // This attribute makes sure that we later know that this foreign item's symbol should not be. @@ -381,13 +424,9 @@ fn generate_attribute_macro_to_implement( macro_name: Ident, foreign_item_name: Ident, impl_unsafe: bool, - attrs_from_decl: &[Attribute], + attrs_from_decl: ThinVec, ) -> Box { - let mut macro_attrs = ThinVec::new(); - - // To avoid e.g. `error: attribute macro has missing stability attribute` - // errors for eii's in std. - macro_attrs.extend_from_slice(attrs_from_decl); + let mut macro_attrs = attrs_from_decl; // Avoid "missing stability attribute" errors for eiis in std. See #146993. macro_attrs.push(ecx.attr_name_value_str(sym::rustc_macro_transparency, sym::semiopaque, span)); diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index c64d6871269a6..929c88402b1aa 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1186,6 +1186,15 @@ pub(crate) struct EiiMacroExpectedMaxOneArgument { pub name: String, } +#[derive(Diagnostic)] +#[diag("only a small subset of attributes are supported on externally implementable items")] +pub(crate) struct EiiAttributeNotSupported { + #[primary_span] + pub span: Span, + #[note("this attribute is not supported")] + pub attr_span: Span, +} + #[derive(Diagnostic)] #[diag("named argument `{$named_arg_name}` is not used by name")] pub(crate) struct NamedArgumentUsedPositionally { diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 2c0a6ff01018c..b746bab643a34 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -169,7 +169,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { ) { for (alias, linkage, visibility) in aliases { let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias)); - tracing::debug!("FUNCTION ALIAS: {alias:?} {linkage:?} {visibility:?}"); + tracing::debug!( + "FUNCTION ALIAS: generating fn {} that calls {aliasee_instance:?} ({alias:?} {linkage:?} {visibility:?})", + symbol_name.name + ); // predefine another copy of the original instance // with a new symbol name diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index b4626724fd803..d1d6499ac2d45 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -259,7 +259,7 @@ fn process_builtin_attrs( codegen_fn_attrs.foreign_item_symbol_aliases.push(( foreign_item, - if i.is_default { Linkage::LinkOnceAny } else { Linkage::External }, + if i.is_default { Linkage::WeakAny } else { Linkage::External }, Visibility::Default, )); codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index b6b95c5f12aae..a5d575fd1b518 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -711,7 +711,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // with #[rustc_inherit_overflow_checks] and inlined from // another crate (mostly core::num generic/#[inline] fns), // while the current crate doesn't use overflow checks. - if !bx.sess().overflow_checks() && msg.is_optional_overflow_check() { + if !bx.sess().overflow_checks().is_checked() && msg.is_optional_overflow_check() { const_cond = Some(expected); } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 4a3615e5421fe..cbc8913b6d67e 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -448,6 +448,8 @@ language_item_table! { // Used to fallback `{float}` to `f32` when `f32: From<{float}>` From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1); + + RecoverOverflow, sym::recover_overflow, recover_integer_overflow, Target::Fn, GenericRequirement::Exact(0); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 8b015e6cecaae..558b1b92f4756 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1588,7 +1588,7 @@ impl RuntimeChecks { match self { Self::UbChecks => sess.ub_checks(), Self::ContractChecks => sess.contract_checks(), - Self::OverflowChecks => sess.overflow_checks(), + Self::OverflowChecks => sess.overflow_checks().is_checked(), } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index ce443d66dc619..fb4afd3f1268f 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -99,7 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.as_operand(block, scope, arg, LocalInfo::Boring, NeedsTemporary::No) ); // Check for -MIN on signed integers - if this.check_overflow && op == UnOp::Neg && expr.ty.is_signed() { + if this.check_overflow.is_checked() && op == UnOp::Neg && expr.ty.is_signed() { let bool_ty = this.tcx.types.bool; let minval = this.minval_literal(expr_span, expr.ty); @@ -112,7 +112,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Rvalue::BinaryOp(BinOp::Eq, Box::new((arg.to_copy(), minval))), ); - block = this.assert( + block = this.overflow_check( block, Operand::Move(is_min), false, @@ -453,7 +453,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = self.source_info(span); let bool_ty = self.tcx.types.bool; let rvalue = match op { - BinOp::Add | BinOp::Sub | BinOp::Mul if self.check_overflow && ty.is_integral() => { + BinOp::Add | BinOp::Sub | BinOp::Mul + if self.check_overflow.is_checked() && ty.is_integral() => + { let result_tup = Ty::new_tup(self.tcx, &[ty, bool_ty]); let result_value = self.temp(result_tup, span); @@ -473,11 +475,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let of = tcx.mk_place_field(result_value, of_fld, bool_ty); let err = AssertKind::Overflow(op, lhs, rhs); - block = self.assert(block, Operand::Move(of), false, err, span); + block = self.overflow_check(block, Operand::Move(of), false, err, span); Rvalue::Use(Operand::Move(val), WithRetag::Yes) } - BinOp::Shl | BinOp::Shr if self.check_overflow && ty.is_integral() => { + BinOp::Shl | BinOp::Shr if self.check_overflow.is_checked() && ty.is_integral() => { // For an unsigned RHS, the shift is in-range for `rhs < bits`. // For a signed RHS, `IntToInt` cast to the equivalent unsigned // type and do that same comparison. @@ -523,7 +525,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); let overflow_err = AssertKind::Overflow(op, lhs.to_copy(), rhs.to_copy()); - block = self.assert(block, Operand::Move(inbounds), true, overflow_err, span); + block = + self.overflow_check(block, Operand::Move(inbounds), true, overflow_err, span); Rvalue::BinaryOp(op, Box::new((lhs, rhs))) } BinOp::Div | BinOp::Rem if ty.is_integral() => { diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index e9f9da9c8cd91..7373f28a8a3c2 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -39,7 +39,7 @@ use rustc_middle::mir::*; use rustc_middle::thir::{self, ExprId, LocalVarId, Param, ParamId, PatKind, Thir}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; -use rustc_session::lint; +use rustc_session::{CheckOverflow, lint}; use rustc_span::{Span, Symbol}; use crate::builder::expr::as_place::PlaceBuilder; @@ -173,7 +173,7 @@ struct Builder<'a, 'tcx> { def_id: LocalDefId, hir_id: HirId, parent_module: DefId, - check_overflow: bool, + check_overflow: CheckOverflow, fn_span: Span, arg_count: usize, coroutine: Option>>, @@ -752,17 +752,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { coroutine: Option>>, ) -> Builder<'a, 'tcx> { let tcx = infcx.tcx; + // Respect -C overflow-checks. + let mut check_overflow = tcx.sess.overflow_checks(); // Some functions always have overflow checks enabled, // however, they may not get codegen'd, depending on // the settings for the crate they are codegened in. - let mut check_overflow = find_attr!(tcx.hir_attrs(hir_id), RustcInheritOverflowChecks); - // Respect -C overflow-checks. - check_overflow |= tcx.sess.overflow_checks(); + if find_attr!(tcx.hir_attrs(hir_id), RustcInheritOverflowChecks) { + check_overflow = CheckOverflow::Checked; + } // Constants always need overflow checks. - check_overflow |= matches!( + if matches!( tcx.hir_body_owner_kind(def), hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_) - ); + ) { + check_overflow = CheckOverflow::Checked; + } let lint_level = LintLevel::Explicit(hir_id); let param_env = tcx.param_env(def); diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 3343bec30caf5..89fd0e5967a2d 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -85,7 +85,7 @@ use std::mem; use interpret::ErrorHandled; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::HirId; +use rustc_hir::{HirId, LangItem}; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::middle::region; use rustc_middle::mir::{self, *}; @@ -93,6 +93,7 @@ use rustc_middle::thir::{AdtExpr, AdtExprBase, ArmId, ExprId, ExprKind}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree}; use rustc_middle::{bug, span_bug}; use rustc_pattern_analysis::rustc::RustcPatCtxt; +use rustc_session::CheckOverflow; use rustc_session::lint::Level; use rustc_span::{DUMMY_SP, Span, Spanned}; use tracing::{debug, instrument}; @@ -1783,6 +1784,69 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block } + pub(crate) fn overflow_check( + &mut self, + block: BasicBlock, + cond: Operand<'tcx>, + expected: bool, + msg: AssertMessage<'tcx>, + span: Span, + ) -> BasicBlock { + match self.check_overflow { + CheckOverflow::Checked => self.assert(block, cond, expected, msg, span), + CheckOverflow::Recoverable => { + self.recoverable_overflow_check(block, cond, expected, msg, span) + } + CheckOverflow::Ignore => unreachable!(), + } + } + + pub(crate) fn recoverable_overflow_check( + &mut self, + block: BasicBlock, + cond: Operand<'tcx>, + expected: bool, + msg: AssertMessage<'tcx>, + span: Span, + ) -> BasicBlock { + assert!( + msg.is_optional_overflow_check(), + "recoverable_overflow_check should only be called with an overflow-related assertion" + ); + + let source_info = self.source_info(span); + + let no_overflow_block = self.cfg.start_new_block(); + let overflow_block = self.cfg.start_new_block(); + + self.cfg.terminate( + block, + source_info, + TerminatorKind::if_(cond, overflow_block, no_overflow_block), + ); + + let recovery_lang_item_def_id = self.tcx.require_lang_item(LangItem::RecoverOverflow, span); + let func = Operand::function_handle(self.tcx, recovery_lang_item_def_id, [], span); + let destination = self.get_unit_temp(); + + self.cfg.terminate( + overflow_block, + source_info, + TerminatorKind::Call { + func, + args: Box::new([]), + destination, + // if this function returns, continue as if no overflow happend! + target: Some(no_overflow_block), + unwind: UnwindAction::Continue, + call_source: CallSource::Misc, + fn_span: span, + }, + ); + + no_overflow_block + } + /// Unschedules any drops in the top two scopes. /// /// This is only needed for pattern-matches combining guards and or-patterns: or-patterns lead diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 331c98fc198eb..68c7596265bd6 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -143,7 +143,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { .opts .unstable_opts .inline_mir_preserve_debug - .unwrap_or(self.tcx.sess.overflow_checks()) + .unwrap_or(self.tcx.sess.overflow_checks().is_checked()) { INSTR_COST } else { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e82f67eac5e9f..78acc4667bdb3 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3052,8 +3052,8 @@ pub(crate) mod dep_tracking { OutputTypes, PatchableFunctionEntry, Polonius, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, }; - use crate::lint; use crate::utils::NativeLib; + use crate::{CheckOverflow, lint}; pub(crate) trait DepTrackingHash { fn hash( @@ -3154,6 +3154,7 @@ pub(crate) mod dep_tracking { InliningThreshold, FunctionReturn, Align, + CheckOverflow, ); impl DepTrackingHash for (T1, T2) diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index d16ab59a02d9e..74320ea399d87 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -31,7 +31,7 @@ use rustc_span::{Symbol, sym}; use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, Target}; use crate::config::{CrateType, FmtDebug}; -use crate::{Session, errors}; +use crate::{CheckOverflow, Session, errors}; /// The parsed `--cfg` options that define the compilation environment of the /// crate, used to drive conditional compilation. @@ -209,8 +209,17 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { } } - if sess.overflow_checks() { - ins_none!(sym::overflow_checks); + match sess.overflow_checks() { + CheckOverflow::Checked => { + ins_none!(sym::overflow_checks); + } + // this treats recoverable also as checked for cfg purposes. + // That's mostly backwards compatible, but may be unintuitive if the overflow ends up recovering, + // and we expected an unwind based on this cfg. + CheckOverflow::Recoverable => { + ins_none!(sym::overflow_checks); + } + CheckOverflow::Ignore => {} } // We insert a cfg for the name of session's panic strategy. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 1b2e24a684bc9..211953ceb89b7 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -23,7 +23,7 @@ use rustc_target::spec::{ use crate::config::*; use crate::search_paths::SearchPath; use crate::utils::NativeLib; -use crate::{EarlyDiagCtxt, Session, lint}; +use crate::{CheckOverflow, EarlyDiagCtxt, Session, lint}; macro_rules! insert { ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr) => { @@ -763,6 +763,7 @@ mod desc { pub(crate) const parse_threads: &str = "a number or `sync`"; pub(crate) const parse_time_passes_format: &str = "`text` (default) or `json`"; pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`"; + pub(crate) const parse_opt_check_overflow: &str = "one of `checked`, `recoverable`, or `ignore`, or one of: `y`, `yes`, `on`, `true`, `n`, `no`, `off` or `false` to select `checked` or `ignore` respectively"; pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`"; pub(crate) const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`"; pub(crate) const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)"; @@ -850,6 +851,7 @@ pub mod parse { use std::str::FromStr; pub(crate) use super::*; + use crate::CheckOverflow; pub(crate) const MAX_THREADS_CAP: usize = 256; /// Ignore the value. Used for removed options where we don't actually want to store @@ -1155,6 +1157,26 @@ pub mod parse { true } + pub(crate) fn parse_opt_check_overflow( + slot: &mut Option, + v: Option<&str>, + ) -> bool { + let mut bool_slot = None; + match v { + Some("checked") => *slot = Some(CheckOverflow::Checked), + Some("recoverable") => *slot = Some(CheckOverflow::Recoverable), + Some("ignore") => *slot = Some(CheckOverflow::Ignore), + _ if parse_opt_bool(&mut bool_slot, v) => { + *slot = bool_slot.map(|i| match i { + true => CheckOverflow::Checked, + false => CheckOverflow::Ignore, + }); + } + _ => return false, + } + true + } + pub(crate) fn parse_panic_strategy(slot: &mut PanicStrategy, v: Option<&str>) -> bool { match v { Some("unwind") => *slot = PanicStrategy::Unwind, @@ -2127,7 +2149,7 @@ options! { opt_level: String = ("0".to_string(), parse_string, [TRACKED], "optimization level (0-3, s, or z; default: 0)"), #[rustc_lint_opt_deny_field_access("use `Session::overflow_checks` instead of this field")] - overflow_checks: Option = (None, parse_opt_bool, [TRACKED], + overflow_checks: Option = (None, parse_opt_check_overflow, [TRACKED], "use overflow checks for integer arithmetic"), #[rustc_lint_opt_deny_field_access("use `Session::panic_strategy` instead of this field")] panic: Option = (None, parse_opt_panic_strategy, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index c83bb62324e76..a8f74868d1a33 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -73,6 +73,29 @@ pub struct Limits { pub pattern_complexity_limit: Limit, } +#[derive(Clone, Copy, Debug, StableHash, PartialEq, Eq, Hash)] +pub enum CheckOverflow { + /// The normal `-C check-overflow` behavior, where overflows panic + Checked, + /// Overflows are checked, and invoke a user-overridable function in core. + /// This function by default panics, but can be overridden by users to keep going instead. + /// Note that the performance cost is similar to `Checked`, *also* when overflows are recovered from. + Recoverable, + /// Overflows are ignored. + Ignore, +} + +impl CheckOverflow { + /// Returns true if some form of overflow checking is enabled. + pub fn is_checked(&self) -> bool { + match self { + CheckOverflow::Checked => true, + CheckOverflow::Recoverable => true, + CheckOverflow::Ignore => false, + } + } +} + pub struct CompilerIO { pub input: Input, pub output_dir: Option, @@ -721,8 +744,12 @@ impl Session { self.opts.unstable_features.is_nightly_build() } - pub fn overflow_checks(&self) -> bool { - self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) + pub fn overflow_checks(&self) -> CheckOverflow { + self.opts.cg.overflow_checks.unwrap_or(if self.opts.debug_assertions { + CheckOverflow::Checked + } else { + CheckOverflow::Ignore + }) } pub fn ub_checks(&self) -> bool { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d220651fc9404..8e6ede6cd3f6e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1627,6 +1627,7 @@ symbols! { reborrow, receiver, receiver_target, + recover_overflow, recursion_limit, reexport_test_harness_main, ref_pat_eat_one_layer_2024, diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index acc758a75e77b..e29a132017685 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -134,6 +134,7 @@ #![feature(diagnostic_on_unmatch_args)] #![feature(doc_cfg)] #![feature(doc_notable_trait)] +#![feature(extern_item_impls)] #![feature(extern_types)] #![feature(f16)] #![feature(f128)] diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index cf07504466808..842d2e4302498 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -199,3 +199,34 @@ pub macro const_assert { } }} } + +// HACK: currently it's hard to make foreign items lang items, +// this lang item is a function instead that serves as a proxy for the foreign item. +#[lang = "recover_overflow"] +#[doc(hidden)] +#[unstable(feature = "recoverable_integer_overflow_internals", issue = "none")] +pub fn call_recover_overflow_handler() { + recoverable_integer_overflow_handler(); +} + +/// Called on overflow when `-Coverflow-checks=recoverable` is set. +/// +/// Panics by default, mirroring `-Coverflow-checks=checked`, but can be overridden not to panic: +/// +/// ``` +/// #![feature(recoverable_integer_overflow)] +/// +/// use core::sync::atomic::{AtomicUsize, Ordering}; +/// +/// static COUNT_OVERFLOWS: AtomicUsize = AtomicUsize::new(0); +/// +/// #[core::panic::integer_overflow_action] +/// fn count_integer_overflows(_fmt: fmt::Arguments<'_>) { +/// COUNT_OVERFLOWS.fetch_add(1, Ordering::Relaxed); +/// } +/// ``` +#[eii(integer_overflow_action)] +#[unstable(feature = "recoverable_integer_overflow", issue = "none")] +fn recoverable_integer_overflow_handler() { + crate::panicking::panic_fmt(format_args!("integer overflow")); +} diff --git a/tests/ui/codegen/recoverable_overflow.rs b/tests/ui/codegen/recoverable_overflow.rs new file mode 100644 index 0000000000000..c22cdc9bbe7c2 --- /dev/null +++ b/tests/ui/codegen/recoverable_overflow.rs @@ -0,0 +1,16 @@ +//@ no-prefer-dynamic +//@ compile-flags: -Coverflow-checks=recoverable +//@ run-pass +//@ check-run-results +#![feature(recoverable_integer_overflow)] +#![allow(arithmetic_overflow, unused)] + +#[core::panic::integer_overflow_action] +fn overflow() { + println!("overflow happened") +} + +fn main() { + let mut x = 255u8; + x += 1; +} diff --git a/tests/ui/codegen/recoverable_overflow.run.stdout b/tests/ui/codegen/recoverable_overflow.run.stdout new file mode 100644 index 0000000000000..b883b5c9f2780 --- /dev/null +++ b/tests/ui/codegen/recoverable_overflow.run.stdout @@ -0,0 +1 @@ +overflow happened diff --git a/tests/ui/eii/attrs.rs b/tests/ui/eii/attrs.rs new file mode 100644 index 0000000000000..ec6259863e9b0 --- /dev/null +++ b/tests/ui/eii/attrs.rs @@ -0,0 +1,22 @@ +#![feature(extern_item_impls)] +#![deny(deprecated)] //~ NOTE: + +// makes no sense on functions, nor on the macro generated (it's a macrov2). +#[macro_export] //~ NOTE: this attribute is not supported +// makes sense, as long as we only forward it onto the function, +// so we allow and this shouln't cause errors for being on a "wrong target". +#[inline] +// makes sense, should be allowed, and forwarded on both the function and the macro +#[deprecated = "foo"] +#[eii] +fn example() {} +//~^ ERROR only a small subset of attributes are supported on externally implementable items + +// check that both are deprecated vvvv +#[example] +//~^ ERROR use of deprecated macro +fn explicit_impl() {} +fn main() { + example() + //~^ ERROR use of deprecated function +} diff --git a/tests/ui/eii/attrs.stderr b/tests/ui/eii/attrs.stderr new file mode 100644 index 0000000000000..a8d7cbbee1828 --- /dev/null +++ b/tests/ui/eii/attrs.stderr @@ -0,0 +1,32 @@ +error: only a small subset of attributes are supported on externally implementable items + --> $DIR/attrs.rs:12:1 + | +LL | fn example() {} + | ^^^^^^^^^^^^ + | +note: this attribute is not supported + --> $DIR/attrs.rs:5:1 + | +LL | #[macro_export] + | ^^^^^^^^^^^^^^^ + +error: use of deprecated macro `example`: foo + --> $DIR/attrs.rs:16:3 + | +LL | #[example] + | ^^^^^^^ + | +note: the lint level is defined here + --> $DIR/attrs.rs:2:9 + | +LL | #![deny(deprecated)] + | ^^^^^^^^^^ + +error: use of deprecated function `example`: foo + --> $DIR/attrs.rs:20:5 + | +LL | example() + | ^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/eii/ice_contract_attr_on_eii_generated_item.rs b/tests/ui/eii/ice_contract_attr_on_eii_generated_item.rs deleted file mode 100644 index fce142f6dc08b..0000000000000 --- a/tests/ui/eii/ice_contract_attr_on_eii_generated_item.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ compile-flags: --crate-type rlib - -#![feature(extern_item_impls)] -#![feature(contracts)] -#![allow(incomplete_features)] - -#[eii] -#[core::contracts::ensures] -//~^ ERROR contract annotations is only supported in functions with bodies -//~| ERROR contract annotations can only be used on functions -fn implementation(); diff --git a/tests/ui/eii/ice_contract_attr_on_eii_generated_item.stderr b/tests/ui/eii/ice_contract_attr_on_eii_generated_item.stderr deleted file mode 100644 index 3335346e55eec..0000000000000 --- a/tests/ui/eii/ice_contract_attr_on_eii_generated_item.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: contract annotations is only supported in functions with bodies - --> $DIR/ice_contract_attr_on_eii_generated_item.rs:8:1 - | -LL | #[core::contracts::ensures] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: contract annotations can only be used on functions - --> $DIR/ice_contract_attr_on_eii_generated_item.rs:8:1 - | -LL | #[core::contracts::ensures] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors -