Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 57 additions & 18 deletions compiler/rustc_builtin_macros/src/eii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -148,6 +150,7 @@ fn eii_(
eii_attr_span,
item_span,
foreign_item_name,
default_func_attrs,
))
}

Expand All @@ -157,22 +160,65 @@ fn eii_(
item_span,
kind,
vis,
&attrs_from_decl,
foreign_item_attrs,
));
module_items.push(generate_attribute_macro_to_implement(
ecx,
eii_attr_span,
macro_name,
foreign_item_name,
impl_unsafe,
&attrs_from_decl,
macro_attrs,
));

// we don't need to wrap in Annotatable::Stmt conditionally since
// EII can't be used on items in statement position
module_items.into_iter().map(Annotatable::Item).collect()
}

fn split_attrs(
ecx: &mut ExtCtxt<'_>,
span: Span,
attrs: ThinVec<Attribute>,
) -> (ThinVec<Attribute>, ThinVec<Attribute>, ThinVec<Attribute>) {
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.
Expand Down Expand Up @@ -228,10 +274,8 @@ fn generate_default_func_impl(
eii_attr_span: Span,
item_span: Span,
foreign_item_name: Ident,
attrs: ThinVec<Attribute>,
) -> Box<ast::Item> {
// 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,
Expand Down Expand Up @@ -289,10 +333,9 @@ fn generate_foreign_item(
item_span: Span,
item_kind: &ItemKind,
vis: Visibility,
attrs_from_decl: &[Attribute],
attrs_from_decl: ThinVec<Attribute>,
) -> Box<ast::Item> {
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.
Expand Down Expand Up @@ -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<Attribute>,
) -> Box<ast::Item> {
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));
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_builtin_macros/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_codegen_llvm/src/mono_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
}
}
Expand Down
15 changes: 9 additions & 6 deletions compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -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);

Expand All @@ -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.
Expand Down Expand Up @@ -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() => {
Expand Down
18 changes: 11 additions & 7 deletions compiler/rustc_mir_build/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Box<CoroutineInfo<'tcx>>>,
Expand Down Expand Up @@ -752,17 +752,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
coroutine: Option<Box<CoroutineInfo<'tcx>>>,
) -> 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);
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_mir_build/src/builder/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -1783,6 +1784,21 @@ 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 => todo!(),
CheckOverflow::Ignore => unreachable!(),
}
}

/// Unschedules any drops in the top two scopes.
///
/// This is only needed for pattern-matches combining guards and or-patterns: or-patterns lead
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/cost_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -3154,6 +3154,7 @@ pub(crate) mod dep_tracking {
InliningThreshold,
FunctionReturn,
Align,
CheckOverflow,
);

impl<T1, T2> DepTrackingHash for (T1, T2)
Expand Down
15 changes: 12 additions & 3 deletions compiler/rustc_session/src/config/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Loading