Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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: 2 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
66 changes: 65 additions & 1 deletion compiler/rustc_mir_build/src/builder/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,15 @@ 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, *};
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,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
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
Loading
Loading