Auto merge of #134353 - oli-obk:safe-target-feature-unsafe-by-default, r=wesleywiser

Treat safe target_feature functions as unsafe by default [less invasive variant]

This unblocks
* #134090

As I stated in https://github.com/rust-lang/rust/pull/134090#issuecomment-2541332415 I think the previous impl was too easy to get wrong, as by default it treated safe target feature functions as safe and had to add additional checks for when they weren't. Now the logic is inverted. By default they are unsafe and you have to explicitly handle safe target feature functions.

This is the less (imo) invasive variant of #134317, as it doesn't require changing the Safety enum, so it only affects FnDefs and nothing else, as it should.
This commit is contained in:
bors 2025-01-15 12:06:56 +00:00
commit 341f60327f
39 changed files with 319 additions and 120 deletions

View file

@ -188,7 +188,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> hir::FnSig<'hir> {
let header = if let Some(local_sig_id) = sig_id.as_local() {
match self.resolver.delegation_fn_sigs.get(&local_sig_id) {
Some(sig) => self.lower_fn_header(sig.header, hir::Safety::Safe),
Some(sig) => self.lower_fn_header(
sig.header,
// HACK: we override the default safety instead of generating attributes from the ether.
// We are not forwarding the attributes, as the delegation fn sigs are collected on the ast,
// and here we need the hir attributes.
if sig.target_feature { hir::Safety::Unsafe } else { hir::Safety::Safe },
&[],
),
None => self.generate_header_error(),
}
} else {
@ -198,7 +205,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
Asyncness::No => hir::IsAsync::NotAsync,
};
hir::FnHeader {
safety: sig.safety,
safety: if self.tcx.codegen_fn_attrs(sig_id).safe_target_features {
hir::HeaderSafety::SafeTargetFeatures
} else {
hir::HeaderSafety::Normal(sig.safety)
},
constness: self.tcx.constness(sig_id),
asyncness,
abi: sig.abi,
@ -384,7 +395,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn generate_header_error(&self) -> hir::FnHeader {
hir::FnHeader {
safety: hir::Safety::Safe,
safety: hir::Safety::Safe.into(),
constness: hir::Constness::NotConst,
asyncness: hir::IsAsync::NotAsync,
abi: abi::Abi::Rust,

View file

@ -231,7 +231,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
});
let sig = hir::FnSig {
decl,
header: this.lower_fn_header(*header, hir::Safety::Safe),
header: this.lower_fn_header(*header, hir::Safety::Safe, attrs),
span: this.lower_span(*fn_sig_span),
};
hir::ItemKind::Fn { sig, generics, body: body_id, has_body: body.is_some() }
@ -610,7 +610,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
let owner_id = hir_id.expect_owner();
self.lower_attrs(hir_id, &i.attrs);
let attrs = self.lower_attrs(hir_id, &i.attrs);
let item = hir::ForeignItem {
owner_id,
ident: self.lower_ident(i.ident),
@ -634,7 +634,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
});
// Unmarked safety in unsafe block defaults to unsafe.
let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe);
let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe, attrs);
hir::ForeignItemKind::Fn(
hir::FnSig { header, decl, span: self.lower_span(sig.span) },
@ -776,6 +776,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.id,
FnDeclKind::Trait,
sig.header.coroutine_kind,
attrs,
);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
}
@ -795,6 +796,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.id,
FnDeclKind::Trait,
sig.header.coroutine_kind,
attrs,
);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
}
@ -911,6 +913,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.id,
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
sig.header.coroutine_kind,
attrs,
);
(generics, hir::ImplItemKind::Fn(sig, body_id))
@ -1339,8 +1342,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
id: NodeId,
kind: FnDeclKind,
coroutine_kind: Option<CoroutineKind>,
attrs: &[hir::Attribute],
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
let header = self.lower_fn_header(sig.header, hir::Safety::Safe);
let header = self.lower_fn_header(sig.header, hir::Safety::Safe, attrs);
let itctx = ImplTraitContext::Universal;
let (generics, decl) = self.lower_generics(generics, id, itctx, |this| {
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
@ -1352,14 +1356,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
h: FnHeader,
default_safety: hir::Safety,
attrs: &[hir::Attribute],
) -> hir::FnHeader {
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind {
hir::IsAsync::Async(span)
} else {
hir::IsAsync::NotAsync
};
let safety = self.lower_safety(h.safety, default_safety);
// Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so.
let safety = if attrs.iter().any(|attr| attr.has_name(sym::target_feature))
&& safety.is_safe()
&& !self.tcx.sess.target.is_like_wasm
{
hir::HeaderSafety::SafeTargetFeatures
} else {
safety.into()
};
hir::FnHeader {
safety: self.lower_safety(h.safety, default_safety),
safety,
asyncness,
constness: self.lower_constness(h.constness),
abi: self.lower_extern(h.ext),

View file

@ -250,10 +250,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
}
sym::target_feature => {
if !tcx.is_closure_like(did.to_def_id())
&& let Some(fn_sig) = fn_sig()
&& fn_sig.skip_binder().safety().is_safe()
{
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
tcx.dcx().span_delayed_bug(attr.span, "target_feature applied to non-fn");
continue;
};
let safe_target_features =
matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
codegen_fn_attrs.safe_target_features = safe_target_features;
if safe_target_features {
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
// The `#[target_feature]` attribute is allowed on
// WebAssembly targets on all functions, including safe

View file

@ -3762,9 +3762,30 @@ impl fmt::Display for Constness {
}
}
/// The actualy safety specified in syntax. We may treat
/// its safety different within the type system to create a
/// "sound by default" system that needs checking this enum
/// explicitly to allow unsafe operations.
#[derive(Copy, Clone, Debug, HashStable_Generic, PartialEq, Eq)]
pub enum HeaderSafety {
/// A safe function annotated with `#[target_features]`.
/// The type system treats this function as an unsafe function,
/// but safety checking will check this enum to treat it as safe
/// and allowing calling other safe target feature functions with
/// the same features without requiring an additional unsafe block.
SafeTargetFeatures,
Normal(Safety),
}
impl From<Safety> for HeaderSafety {
fn from(v: Safety) -> Self {
Self::Normal(v)
}
}
#[derive(Copy, Clone, Debug, HashStable_Generic)]
pub struct FnHeader {
pub safety: Safety,
pub safety: HeaderSafety,
pub constness: Constness,
pub asyncness: IsAsync,
pub abi: ExternAbi,
@ -3780,7 +3801,18 @@ impl FnHeader {
}
pub fn is_unsafe(&self) -> bool {
self.safety.is_unsafe()
self.safety().is_unsafe()
}
pub fn is_safe(&self) -> bool {
self.safety().is_safe()
}
pub fn safety(&self) -> Safety {
match self.safety {
HeaderSafety::SafeTargetFeatures => Safety::Unsafe,
HeaderSafety::Normal(safety) => safety,
}
}
}

View file

@ -1336,7 +1336,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
{
icx.lowerer().lower_fn_ty(
hir_id,
sig.header.safety,
sig.header.safety(),
sig.header.abi,
sig.decl,
Some(generics),
@ -1351,13 +1351,18 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _),
generics,
..
}) => {
icx.lowerer().lower_fn_ty(hir_id, header.safety, header.abi, decl, Some(generics), None)
}
}) => icx.lowerer().lower_fn_ty(
hir_id,
header.safety(),
header.abi,
decl,
Some(generics),
None,
),
ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(sig, _, _), .. }) => {
let abi = tcx.hir().get_foreign_abi(hir_id);
compute_sig_of_foreign_fn_decl(tcx, def_id, sig.decl, abi, sig.header.safety)
compute_sig_of_foreign_fn_decl(tcx, def_id, sig.decl, abi, sig.header.safety())
}
Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor().is_some() => {
@ -1405,7 +1410,7 @@ fn lower_fn_sig_recovering_infer_ret_ty<'tcx>(
icx.lowerer().lower_fn_ty(
icx.tcx().local_def_id_to_hir_id(def_id),
sig.header.safety,
sig.header.safety(),
sig.header.abi,
sig.decl,
Some(generics),

View file

@ -2407,7 +2407,7 @@ impl<'a> State<'a> {
self.print_fn(
decl,
hir::FnHeader {
safety,
safety: safety.into(),
abi,
constness: hir::Constness::NotConst,
asyncness: hir::IsAsync::NotAsync,
@ -2423,12 +2423,20 @@ impl<'a> State<'a> {
fn print_fn_header_info(&mut self, header: hir::FnHeader) {
self.print_constness(header.constness);
let safety = match header.safety {
hir::HeaderSafety::SafeTargetFeatures => {
self.word_nbsp("#[target_feature]");
hir::Safety::Safe
}
hir::HeaderSafety::Normal(safety) => safety,
};
match header.asyncness {
hir::IsAsync::NotAsync => {}
hir::IsAsync::Async(_) => self.word_nbsp("async"),
}
self.print_safety(header.safety);
self.print_safety(safety);
if header.abi != ExternAbi::Rust {
self.word_nbsp("extern");

View file

@ -932,10 +932,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
return Err(TypeError::ForceInlineCast);
}
// Safe `#[target_feature]` functions are not assignable to safe fn pointers
// (RFC 2396).
let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
if matches!(fn_attrs.inline, InlineAttr::Force { .. }) {
return Err(TypeError::ForceInlineCast);
}
// FIXME(target_feature): Safe `#[target_feature]` functions could be cast to safe fn pointers (RFC 2396),
// as you can already write that "cast" in user code by wrapping a target_feature fn call in a closure,
// which is safe. This is sound because you already need to be executing code that is satisfying the target
// feature constraints..
if b_hdr.safety.is_safe()
&& !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
&& self.tcx.codegen_fn_attrs(def_id).safe_target_features
{
return Err(TypeError::TargetFeatureCast(def_id));
}

View file

@ -139,7 +139,7 @@ fn typeck_with_fallback<'tcx>(
// type that has an infer in it, lower the type directly so that it'll
// be correctly filled with infer. We'll use this inference to provide
// a suggestion later on.
fcx.lowerer().lower_fn_ty(id, header.safety, header.abi, decl, None, None)
fcx.lowerer().lower_fn_ty(id, header.safety(), header.abi, decl, None, None)
} else {
tcx.fn_sig(def_id).instantiate_identity()
};

View file

@ -30,6 +30,8 @@ pub struct CodegenFnAttrs {
/// features (only enabled features are supported right now).
/// Implied target features have already been applied.
pub target_features: Vec<TargetFeature>,
/// Whether the function was declared safe, but has target features
pub safe_target_features: bool,
/// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found.
pub linkage: Option<Linkage>,
/// The `#[linkage = "..."]` attribute on foreign items and the value we found.
@ -150,6 +152,7 @@ impl CodegenFnAttrs {
link_name: None,
link_ordinal: None,
target_features: vec![],
safe_target_features: false,
linkage: None,
import_linkage: None,
link_section: None,

View file

@ -222,6 +222,7 @@ pub struct DelegationFnSig {
pub param_count: usize,
pub has_self: bool,
pub c_variadic: bool,
pub target_feature: bool,
}
#[derive(Clone, Copy, Debug)]

View file

@ -690,7 +690,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
if with_reduced_queries() {
p!(print_def_path(def_id, args));
} else {
let sig = self.tcx().fn_sig(def_id).instantiate(self.tcx(), args);
let mut sig = self.tcx().fn_sig(def_id).instantiate(self.tcx(), args);
if self.tcx().codegen_fn_attrs(def_id).safe_target_features {
p!("#[target_features] ");
sig = sig.map_bound(|mut sig| {
sig.safety = hir::Safety::Safe;
sig
});
}
p!(print(sig), " {{", print_value_path(def_id, args), "}}");
}
}

View file

@ -478,19 +478,27 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
return; // don't visit the whole expression
}
ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
if self.thir[fun].ty.fn_sig(self.tcx).safety().is_unsafe() {
let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() {
let fn_ty = self.thir[fun].ty;
let sig = fn_ty.fn_sig(self.tcx);
let (callee_features, safe_target_features): (&[_], _) = match fn_ty.kind() {
ty::FnDef(func_id, ..) => {
let cg_attrs = self.tcx.codegen_fn_attrs(func_id);
(&cg_attrs.target_features, cg_attrs.safe_target_features)
}
_ => (&[], false),
};
if sig.safety().is_unsafe() && !safe_target_features {
let func_id = if let ty::FnDef(func_id, _) = fn_ty.kind() {
Some(*func_id)
} else {
None
};
self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
} else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
} else if let &ty::FnDef(func_did, _) = fn_ty.kind() {
// If the called function has target features the calling function hasn't,
// the call requires `unsafe`. Don't check this on wasm
// targets, though. For more information on wasm see the
// is_like_wasm check in hir_analysis/src/collect.rs
let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
if !self.tcx.sess.target.options.is_like_wasm
&& !callee_features.iter().all(|feature| {
self.body_target_features.iter().any(|f| f.name == feature.name)
@ -739,7 +747,10 @@ impl UnsafeOpKind {
) {
let parent_id = tcx.hir().get_parent_item(hir_id);
let parent_owner = tcx.hir_owner_node(parent_id);
let should_suggest = parent_owner.fn_sig().is_some_and(|sig| sig.header.is_unsafe());
let should_suggest = parent_owner.fn_sig().is_some_and(|sig| {
// Do not suggest for safe target_feature functions
matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe))
});
let unsafe_not_inherited_note = if should_suggest {
suggest_unsafe_block.then(|| {
let body_span = tcx.hir().body(parent_owner.body_id().unwrap()).value.span;
@ -902,7 +913,7 @@ impl UnsafeOpKind {
{
true
} else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id)
&& sig.header.is_unsafe()
&& matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe))
{
true
} else {
@ -1111,7 +1122,16 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
let hir_id = tcx.local_def_id_to_hir_id(def);
let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
if fn_sig.header.safety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe }
match fn_sig.header.safety {
// We typeck the body as safe, but otherwise treat it as unsafe everywhere else.
// Call sites to other SafeTargetFeatures functions are checked explicitly and don't need
// to care about safety of the body.
hir::HeaderSafety::SafeTargetFeatures => SafetyContext::Safe,
hir::HeaderSafety::Normal(safety) => match safety {
hir::Safety::Unsafe => SafetyContext::UnsafeFn,
hir::Safety::Safe => SafetyContext::Safe,
},
}
});
let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features;
let mut warnings = Vec::new();

View file

@ -5019,12 +5019,13 @@ struct ItemInfoCollector<'a, 'ra, 'tcx> {
}
impl ItemInfoCollector<'_, '_, '_> {
fn collect_fn_info(&mut self, sig: &FnSig, id: NodeId) {
fn collect_fn_info(&mut self, sig: &FnSig, id: NodeId, attrs: &[Attribute]) {
let sig = DelegationFnSig {
header: sig.header,
param_count: sig.decl.inputs.len(),
has_self: sig.decl.has_self(),
c_variadic: sig.decl.c_variadic(),
target_feature: attrs.iter().any(|attr| attr.has_name(sym::target_feature)),
};
self.r.delegation_fn_sigs.insert(self.r.local_def_id(id), sig);
}
@ -5043,7 +5044,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> {
| ItemKind::Trait(box Trait { ref generics, .. })
| ItemKind::TraitAlias(ref generics, _) => {
if let ItemKind::Fn(box Fn { ref sig, .. }) = &item.kind {
self.collect_fn_info(sig, item.id);
self.collect_fn_info(sig, item.id, &item.attrs);
}
let def_id = self.r.local_def_id(item.id);
@ -5076,7 +5077,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> {
fn visit_assoc_item(&mut self, item: &'ast AssocItem, ctxt: AssocCtxt) {
if let AssocItemKind::Fn(box Fn { ref sig, .. }) = &item.kind {
self.collect_fn_info(sig, item.id);
self.collect_fn_info(sig, item.id, &item.attrs);
}
visit::walk_assoc_item(self, item, ctxt);
}

View file

@ -824,9 +824,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn cmp_fn_sig(
&self,
sig1: &ty::PolyFnSig<'tcx>,
fn_def1: Option<(DefId, &'tcx [ty::GenericArg<'tcx>])>,
fn_def1: Option<(DefId, Option<&'tcx [ty::GenericArg<'tcx>]>)>,
sig2: &ty::PolyFnSig<'tcx>,
fn_def2: Option<(DefId, &'tcx [ty::GenericArg<'tcx>])>,
fn_def2: Option<(DefId, Option<&'tcx [ty::GenericArg<'tcx>]>)>,
) -> (DiagStyledString, DiagStyledString) {
let sig1 = &(self.normalize_fn_sig)(*sig1);
let sig2 = &(self.normalize_fn_sig)(*sig2);
@ -850,8 +850,20 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
// ^^^^^^
values.0.push(sig1.safety.prefix_str(), sig1.safety != sig2.safety);
values.1.push(sig2.safety.prefix_str(), sig1.safety != sig2.safety);
let safety = |fn_def, sig: ty::FnSig<'_>| match fn_def {
None => sig.safety.prefix_str(),
Some((did, _)) => {
if self.tcx.codegen_fn_attrs(did).safe_target_features {
"#[target_features] "
} else {
sig.safety.prefix_str()
}
}
};
let safety1 = safety(fn_def1, sig1);
let safety2 = safety(fn_def2, sig2);
values.0.push(safety1, safety1 != safety2);
values.1.push(safety2, safety1 != safety2);
// unsafe extern "C" for<'a> fn(&'a T) -> &'a T
// ^^^^^^^^^^
@ -932,23 +944,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
(values.1).0.extend(x2.0);
}
let fmt = |(did, args)| format!(" {{{}}}", self.tcx.def_path_str_with_args(did, args));
let fmt = |did, args| format!(" {{{}}}", self.tcx.def_path_str_with_args(did, args));
match (fn_def1, fn_def2) {
(None, None) => {}
(Some(fn_def1), Some(fn_def2)) => {
let path1 = fmt(fn_def1);
let path2 = fmt(fn_def2);
(Some((fn_def1, Some(fn_args1))), Some((fn_def2, Some(fn_args2)))) => {
let path1 = fmt(fn_def1, fn_args1);
let path2 = fmt(fn_def2, fn_args2);
let same_path = path1 == path2;
values.0.push(path1, !same_path);
values.1.push(path2, !same_path);
}
(Some(fn_def1), None) => {
values.0.push_highlighted(fmt(fn_def1));
(Some((fn_def1, Some(fn_args1))), None) => {
values.0.push_highlighted(fmt(fn_def1, fn_args1));
}
(None, Some(fn_def2)) => {
values.1.push_highlighted(fmt(fn_def2));
(None, Some((fn_def2, Some(fn_args2)))) => {
values.1.push_highlighted(fmt(fn_def2, fn_args2));
}
_ => {}
}
values
@ -1339,17 +1351,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
(ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1);
let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2);
self.cmp_fn_sig(&sig1, Some((*did1, args1)), &sig2, Some((*did2, args2)))
self.cmp_fn_sig(
&sig1,
Some((*did1, Some(args1))),
&sig2,
Some((*did2, Some(args2))),
)
}
(ty::FnDef(did1, args1), ty::FnPtr(sig_tys2, hdr2)) => {
let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1);
self.cmp_fn_sig(&sig1, Some((*did1, args1)), &sig_tys2.with(*hdr2), None)
self.cmp_fn_sig(&sig1, Some((*did1, Some(args1))), &sig_tys2.with(*hdr2), None)
}
(ty::FnPtr(sig_tys1, hdr1), ty::FnDef(did2, args2)) => {
let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2);
self.cmp_fn_sig(&sig_tys1.with(*hdr1), None, &sig2, Some((*did2, args2)))
self.cmp_fn_sig(&sig_tys1.with(*hdr1), None, &sig2, Some((*did2, Some(args2))))
}
(ty::FnPtr(sig_tys1, hdr1), ty::FnPtr(sig_tys2, hdr2)) => {
@ -1531,7 +1548,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
(false, Mismatch::Fixed("existential projection"))
}
};
let Some(vals) = self.values_str(values) else {
let Some(vals) = self.values_str(values, cause) else {
// Derived error. Cancel the emitter.
// NOTE(eddyb) this was `.cancel()`, but `diag`
// is borrowed, so we can't fully defuse it.
@ -1956,7 +1973,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
})
| ObligationCauseCode::BlockTailExpression(.., source)) = code
&& let hir::MatchSource::TryDesugar(_) = source
&& let Some((expected_ty, found_ty, _)) = self.values_str(trace.values)
&& let Some((expected_ty, found_ty, _)) = self.values_str(trace.values, &trace.cause)
{
suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert {
found: found_ty.content(),
@ -2085,6 +2102,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn values_str(
&self,
values: ValuePairs<'tcx>,
cause: &ObligationCause<'tcx>,
) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> {
match values {
ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found),
@ -2109,7 +2127,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
if exp_found.references_error() {
return None;
}
let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, None, &exp_found.found, None);
let (fn_def1, fn_def2) = if let ObligationCauseCode::CompareImplItem {
impl_item_def_id,
trait_item_def_id,
..
} = *cause.code()
{
(Some((trait_item_def_id, None)), Some((impl_item_def_id.to_def_id(), None)))
} else {
(None, None)
};
let (exp, fnd) =
self.cmp_fn_sig(&exp_found.expected, fn_def1, &exp_found.found, fn_def2);
Some((exp, fnd, None))
}
}

View file

@ -461,9 +461,11 @@ impl<T> Trait<T> for X {
(ty::FnPtr(_, hdr), ty::FnDef(def_id, _))
| (ty::FnDef(def_id, _), ty::FnPtr(_, hdr)) => {
if tcx.fn_sig(def_id).skip_binder().safety() < hdr.safety {
diag.note(
if !tcx.codegen_fn_attrs(def_id).safe_target_features {
diag.note(
"unsafe functions cannot be coerced into safe function pointers",
);
);
}
}
}
(ty::Adt(_, _), ty::Adt(def, args))

View file

@ -221,7 +221,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
span: trace.cause.span,
requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
expected_found: self.values_str(trace.values, &trace.cause).map(|(e, f, _)| (e, f)),
}
.add_to_diag(err),
infer::Reborrow(span) => {
@ -946,8 +946,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
if let infer::Subtype(ref sup_trace) = sup_origin
&& let infer::Subtype(ref sub_trace) = sub_origin
&& let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values)
&& let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values)
&& let Some((sup_expected, sup_found, _)) =
self.values_str(sup_trace.values, &sup_trace.cause)
&& let Some((sub_expected, sub_found, _)) =
self.values_str(sub_trace.values, &sup_trace.cause)
&& sub_expected == sup_expected
&& sub_found == sup_found
{

View file

@ -205,9 +205,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
if self_ty.is_fn() {
let fn_sig = self_ty.fn_sig(self.tcx);
let shortname = match fn_sig.safety() {
hir::Safety::Safe => "fn",
hir::Safety::Unsafe => "unsafe fn",
let shortname = if let ty::FnDef(def_id, _) = self_ty.kind()
&& self.tcx.codegen_fn_attrs(def_id).safe_target_features
{
"#[target_feature] fn"
} else {
match fn_sig.safety() {
hir::Safety::Safe => "fn",
hir::Safety::Unsafe => "unsafe fn",
}
};
flags.push((sym::_Self, Some(shortname.to_owned())));
}

View file

@ -3094,7 +3094,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
let kind = match item.kind {
hir::ForeignItemKind::Fn(sig, names, generics) => ForeignFunctionItem(
clean_function(cx, &sig, generics, FunctionArgs::Names(names)),
sig.header.safety,
sig.header.safety(),
),
hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem(
Static { type_: Box::new(clean_ty(ty, cx)), mutability, expr: None },

View file

@ -668,17 +668,28 @@ impl Item {
ty::Asyncness::Yes => hir::IsAsync::Async(DUMMY_SP),
ty::Asyncness::No => hir::IsAsync::NotAsync,
};
hir::FnHeader { safety: sig.safety(), abi: sig.abi(), constness, asyncness }
hir::FnHeader {
safety: if tcx.codegen_fn_attrs(def_id).safe_target_features {
hir::HeaderSafety::SafeTargetFeatures
} else {
sig.safety().into()
},
abi: sig.abi(),
constness,
asyncness,
}
}
let header = match self.kind {
ItemKind::ForeignFunctionItem(_, safety) => {
let def_id = self.def_id().unwrap();
let abi = tcx.fn_sig(def_id).skip_binder().abi();
hir::FnHeader {
safety: if abi == ExternAbi::RustIntrinsic {
intrinsic_operation_unsafety(tcx, def_id.expect_local())
safety: if tcx.codegen_fn_attrs(def_id).safe_target_features {
hir::HeaderSafety::SafeTargetFeatures
} else if abi == ExternAbi::RustIntrinsic {
intrinsic_operation_unsafety(tcx, def_id.expect_local()).into()
} else {
safety
safety.into()
},
abi,
constness: if tcx.is_const_fn(def_id) {

View file

@ -1637,6 +1637,15 @@ impl PrintWithSpace for hir::Safety {
}
}
impl PrintWithSpace for hir::HeaderSafety {
fn print_with_space(&self) -> &str {
match self {
hir::HeaderSafety::SafeTargetFeatures => "",
hir::HeaderSafety::Normal(safety) => safety.print_with_space(),
}
}
}
impl PrintWithSpace for hir::IsAsync {
fn print_with_space(&self) -> &str {
match self {

View file

@ -469,7 +469,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
let unsafety_flag = match myitem.kind {
clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
if myitem.fn_header(tcx).unwrap().safety.is_unsafe() =>
if myitem.fn_header(tcx).unwrap().is_unsafe() =>
{
"<sup title=\"unsafe function\">⚠</sup>"
}

View file

@ -419,7 +419,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
id: LocalDefId,
) -> Self::Result {
if let Some(header) = kind.header()
&& header.safety.is_unsafe()
&& header.is_unsafe()
{
ControlFlow::Break(())
} else {

View file

@ -32,7 +32,7 @@ pub fn check(
}
let span = cx.tcx.def_span(owner_id);
match (headers.safety, sig.header.safety) {
match (headers.safety, sig.header.safety()) {
(false, Safety::Unsafe) => span_lint(
cx,
MISSING_SAFETY_DOC,

View file

@ -34,7 +34,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
ImplicitSelfKind::None => return,
};
let name = if sig.header.safety.is_unsafe() {
let name = if sig.header.is_unsafe() {
name.strip_suffix("_unchecked").unwrap_or(name)
} else {
name

View file

@ -20,8 +20,8 @@ pub(super) fn check_fn<'tcx>(
def_id: LocalDefId,
) {
let safety = match kind {
intravisit::FnKind::ItemFn(_, _, hir::FnHeader { safety, .. }) => safety,
intravisit::FnKind::Method(_, sig) => sig.header.safety,
intravisit::FnKind::ItemFn(_, _, header) => header.safety(),
intravisit::FnKind::Method(_, sig) => sig.header.safety(),
intravisit::FnKind::Closure => return,
};
@ -31,7 +31,7 @@ pub(super) fn check_fn<'tcx>(
pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind {
let body = cx.tcx.hir().body(eid);
check_raw_ptr(cx, sig.header.safety, sig.decl, body, item.owner_id.def_id);
check_raw_ptr(cx, sig.header.safety(), sig.decl, body, item.owner_id.def_id);
}
}

View file

@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
if let ImplItemKind::Fn(ref signature, _) = impl_item.kind
// #11201
&& let header = signature.header
&& header.safety.is_safe()
&& header.is_safe()
&& header.abi == Abi::Rust
&& impl_item.ident.name == sym::to_string
&& let decl = signature.decl

View file

@ -5309,7 +5309,7 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr
}
const FN_HEADER: hir::FnHeader = hir::FnHeader {
safety: hir::Safety::Safe,
safety: hir::HeaderSafety::Normal(hir::Safety::Safe),
constness: hir::Constness::NotConst,
asyncness: hir::IsAsync::NotAsync,
abi: rustc_target::spec::abi::Abi::Rust,

View file

@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
let name = impl_item.ident.name;
let id = impl_item.owner_id;
if sig.header.safety.is_unsafe() {
if sig.header.is_unsafe() {
// can't be implemented for unsafe new
return;
}

View file

@ -541,7 +541,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio
.collect();
if let Some(args) = args
&& !args.is_empty()
&& body.is_none_or(|body| sig.header.safety.is_unsafe() || contains_unsafe_block(cx, body.value))
&& body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value))
{
span_lint_and_then(
cx,

View file

@ -1,11 +1,12 @@
error[E0277]: the trait bound `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}: AsyncFn()` is not satisfied
error[E0277]: the trait bound `#[target_features] fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}: AsyncFn()` is not satisfied
--> $DIR/fn-exception-target-features.rs:16:10
|
LL | test(target_feature);
| ---- ^^^^^^^^^^^^^^ the trait `AsyncFn()` is not implemented for fn item `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}`
| ---- ^^^^^^^^^^^^^^ unsatisfied trait bound
| |
| required by a bound introduced by this call
|
= help: the trait `AsyncFn()` is not implemented for fn item `#[target_features] fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}`
note: required by a bound in `test`
--> $DIR/fn-exception-target-features.rs:13:17
|

View file

@ -3,8 +3,6 @@ fn main() {
.map(
#[target_feature(enable = "")]
//~^ ERROR: attribute should be applied to a function
//~| ERROR: feature named `` is not valid
//~| NOTE: `` is not valid for this target
#[track_caller]
//~^ ERROR: `#[track_caller]` on closures is currently unstable
//~| NOTE: see issue #87417

View file

@ -7,14 +7,8 @@ LL | #[target_feature(enable = "")]
LL | |_| (),
| ------ not a function definition
error: the feature named `` is not valid for this target
--> $DIR/issue-68060.rs:4:30
|
LL | #[target_feature(enable = "")]
| ^^^^^^^^^^^ `` is not valid for this target
error[E0658]: `#[track_caller]` on closures is currently unstable
--> $DIR/issue-68060.rs:8:13
--> $DIR/issue-68060.rs:6:13
|
LL | #[track_caller]
| ^^^^^^^^^^^^^^^
@ -23,6 +17,6 @@ LL | #[track_caller]
= help: add `#![feature(closure_track_caller)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 3 previous errors
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -10,13 +10,8 @@ LL | let foo: fn() = foo;
| expected due to this
|
= note: expected fn pointer `fn()`
found fn item `fn() {foo}`
= note: fn items are distinct from fn pointers
found fn item `#[target_features] fn() {foo}`
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
help: consider casting to a fn pointer
|
LL | let foo: fn() = foo as fn();
| ~~~~~~~~~~~
error: aborting due to 1 previous error

View file

@ -21,9 +21,9 @@ fn call_once(f: impl FnOnce()) {
}
fn main() {
call(foo); //~ ERROR expected a `Fn()` closure, found `fn() {foo}`
call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `fn() {foo}`
call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `fn() {foo}`
call(foo); //~ ERROR expected a `Fn()` closure, found `#[target_features] fn() {foo}`
call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `#[target_features] fn() {foo}`
call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `#[target_features] fn() {foo}`
call(foo_unsafe);
//~^ ERROR expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}`

View file

@ -1,13 +1,13 @@
error[E0277]: expected a `Fn()` closure, found `fn() {foo}`
error[E0277]: expected a `Fn()` closure, found `#[target_features] fn() {foo}`
--> $DIR/fn-traits.rs:24:10
|
LL | call(foo);
| ---- ^^^ expected an `Fn()` closure, found `fn() {foo}`
| ---- ^^^ expected an `Fn()` closure, found `#[target_features] fn() {foo}`
| |
| required by a bound introduced by this call
|
= help: the trait `Fn()` is not implemented for fn item `fn() {foo}`
= note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
= help: the trait `Fn()` is not implemented for fn item `#[target_features] fn() {foo}`
= note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
= note: `#[target_feature]` functions do not implement the `Fn` traits
note: required by a bound in `call`
--> $DIR/fn-traits.rs:11:17
@ -15,16 +15,16 @@ note: required by a bound in `call`
LL | fn call(f: impl Fn()) {
| ^^^^ required by this bound in `call`
error[E0277]: expected a `FnMut()` closure, found `fn() {foo}`
error[E0277]: expected a `FnMut()` closure, found `#[target_features] fn() {foo}`
--> $DIR/fn-traits.rs:25:14
|
LL | call_mut(foo);
| -------- ^^^ expected an `FnMut()` closure, found `fn() {foo}`
| -------- ^^^ expected an `FnMut()` closure, found `#[target_features] fn() {foo}`
| |
| required by a bound introduced by this call
|
= help: the trait `FnMut()` is not implemented for fn item `fn() {foo}`
= note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
= help: the trait `FnMut()` is not implemented for fn item `#[target_features] fn() {foo}`
= note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
= note: `#[target_feature]` functions do not implement the `Fn` traits
note: required by a bound in `call_mut`
--> $DIR/fn-traits.rs:15:25
@ -32,16 +32,16 @@ note: required by a bound in `call_mut`
LL | fn call_mut(mut f: impl FnMut()) {
| ^^^^^^^ required by this bound in `call_mut`
error[E0277]: expected a `FnOnce()` closure, found `fn() {foo}`
error[E0277]: expected a `FnOnce()` closure, found `#[target_features] fn() {foo}`
--> $DIR/fn-traits.rs:26:15
|
LL | call_once(foo);
| --------- ^^^ expected an `FnOnce()` closure, found `fn() {foo}`
| --------- ^^^ expected an `FnOnce()` closure, found `#[target_features] fn() {foo}`
| |
| required by a bound introduced by this call
|
= help: the trait `FnOnce()` is not implemented for fn item `fn() {foo}`
= note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
= help: the trait `FnOnce()` is not implemented for fn item `#[target_features] fn() {foo}`
= note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
= note: `#[target_feature]` functions do not implement the `Fn` traits
note: required by a bound in `call_once`
--> $DIR/fn-traits.rs:19:22

View file

@ -13,6 +13,7 @@ impl Foo for Bar {
#[target_feature(enable = "sse2")]
//~^ ERROR cannot be applied to safe trait method
fn foo(&self) {}
//~^ ERROR method `foo` has an incompatible type for trait
#[target_feature(enable = "sse2")]
unsafe fn unsf_foo(&self) {}

View file

@ -7,8 +7,22 @@ LL |
LL | fn foo(&self) {}
| ------------- not an `unsafe` function
error[E0053]: method `foo` has an incompatible type for trait
--> $DIR/trait-impl.rs:15:5
|
LL | fn foo(&self) {}
| ^^^^^^^^^^^^^ expected safe fn, found unsafe fn
|
note: type in trait
--> $DIR/trait-impl.rs:6:5
|
LL | fn foo(&self);
| ^^^^^^^^^^^^^^
= note: expected signature `fn(&Bar)`
found signature `#[target_features] fn(&Bar)`
error: `#[target_feature(..)]` cannot be applied to safe trait method
--> $DIR/trait-impl.rs:22:5
--> $DIR/trait-impl.rs:23:5
|
LL | #[target_feature(enable = "sse2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method
@ -16,5 +30,6 @@ LL |
LL | fn foo(&self) {}
| ------------- not an `unsafe` function
error: aborting due to 2 previous errors
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0053`.

View file

@ -97,6 +97,7 @@ impl Foo {}
trait Quux {
fn foo(); //~ NOTE `foo` from trait
//~^ NOTE: type in trait
}
impl Quux for Foo {
@ -106,6 +107,9 @@ impl Quux for Foo {
//~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
fn foo() {}
//~^ NOTE not an `unsafe` function
//~| ERROR: incompatible type for trait
//~| NOTE: expected safe fn, found unsafe fn
//~| NOTE: expected signature `fn()`
}
fn main() {

View file

@ -126,7 +126,7 @@ LL | impl Foo {}
| ----------- not a function definition
error: attribute should be applied to a function definition
--> $DIR/invalid-attribute.rs:112:5
--> $DIR/invalid-attribute.rs:116:5
|
LL | #[target_feature(enable = "sse2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -138,7 +138,7 @@ LL | | }
| |_____- not a function definition
error: attribute should be applied to a function definition
--> $DIR/invalid-attribute.rs:120:5
--> $DIR/invalid-attribute.rs:124:5
|
LL | #[target_feature(enable = "sse2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -193,7 +193,7 @@ LL | fn foo();
| --------- `foo` from trait
error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
--> $DIR/invalid-attribute.rs:103:5
--> $DIR/invalid-attribute.rs:104:5
|
LL | #[target_feature(enable = "sse2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -205,7 +205,21 @@ LL | fn foo() {}
= help: add `#![feature(target_feature_11)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 23 previous errors
error[E0053]: method `foo` has an incompatible type for trait
--> $DIR/invalid-attribute.rs:108:5
|
LL | fn foo() {}
| ^^^^^^^^ expected safe fn, found unsafe fn
|
note: type in trait
--> $DIR/invalid-attribute.rs:99:5
|
LL | fn foo();
| ^^^^^^^^^
= note: expected signature `fn()`
found signature `#[target_features] fn()`
Some errors have detailed explanations: E0046, E0658.
error: aborting due to 24 previous errors
Some errors have detailed explanations: E0046, E0053, E0658.
For more information about an error, try `rustc --explain E0046`.