1
Fork 0

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> { ) -> hir::FnSig<'hir> {
let header = if let Some(local_sig_id) = sig_id.as_local() { let header = if let Some(local_sig_id) = sig_id.as_local() {
match self.resolver.delegation_fn_sigs.get(&local_sig_id) { 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(), None => self.generate_header_error(),
} }
} else { } else {
@ -198,7 +205,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
Asyncness::No => hir::IsAsync::NotAsync, Asyncness::No => hir::IsAsync::NotAsync,
}; };
hir::FnHeader { 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), constness: self.tcx.constness(sig_id),
asyncness, asyncness,
abi: sig.abi, abi: sig.abi,
@ -384,7 +395,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn generate_header_error(&self) -> hir::FnHeader { fn generate_header_error(&self) -> hir::FnHeader {
hir::FnHeader { hir::FnHeader {
safety: hir::Safety::Safe, safety: hir::Safety::Safe.into(),
constness: hir::Constness::NotConst, constness: hir::Constness::NotConst,
asyncness: hir::IsAsync::NotAsync, asyncness: hir::IsAsync::NotAsync,
abi: abi::Abi::Rust, abi: abi::Abi::Rust,

View file

@ -231,7 +231,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}); });
let sig = hir::FnSig { let sig = hir::FnSig {
decl, 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), span: this.lower_span(*fn_sig_span),
}; };
hir::ItemKind::Fn { sig, generics, body: body_id, has_body: body.is_some() } 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> { 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 hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
let owner_id = hir_id.expect_owner(); 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 { let item = hir::ForeignItem {
owner_id, owner_id,
ident: self.lower_ident(i.ident), ident: self.lower_ident(i.ident),
@ -634,7 +634,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}); });
// Unmarked safety in unsafe block defaults to unsafe. // 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::ForeignItemKind::Fn(
hir::FnSig { header, decl, span: self.lower_span(sig.span) }, hir::FnSig { header, decl, span: self.lower_span(sig.span) },
@ -776,6 +776,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.id, i.id,
FnDeclKind::Trait, FnDeclKind::Trait,
sig.header.coroutine_kind, sig.header.coroutine_kind,
attrs,
); );
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false) (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
} }
@ -795,6 +796,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.id, i.id,
FnDeclKind::Trait, FnDeclKind::Trait,
sig.header.coroutine_kind, sig.header.coroutine_kind,
attrs,
); );
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true) (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
} }
@ -911,6 +913,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.id, i.id,
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
sig.header.coroutine_kind, sig.header.coroutine_kind,
attrs,
); );
(generics, hir::ImplItemKind::Fn(sig, body_id)) (generics, hir::ImplItemKind::Fn(sig, body_id))
@ -1339,8 +1342,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
id: NodeId, id: NodeId,
kind: FnDeclKind, kind: FnDeclKind,
coroutine_kind: Option<CoroutineKind>, coroutine_kind: Option<CoroutineKind>,
attrs: &[hir::Attribute],
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { ) -> (&'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 itctx = ImplTraitContext::Universal;
let (generics, decl) = self.lower_generics(generics, id, itctx, |this| { let (generics, decl) = self.lower_generics(generics, id, itctx, |this| {
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind) this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
@ -1352,14 +1356,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self, &mut self,
h: FnHeader, h: FnHeader,
default_safety: hir::Safety, default_safety: hir::Safety,
attrs: &[hir::Attribute],
) -> hir::FnHeader { ) -> hir::FnHeader {
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind { let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind {
hir::IsAsync::Async(span) hir::IsAsync::Async(span)
} else { } else {
hir::IsAsync::NotAsync 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 { hir::FnHeader {
safety: self.lower_safety(h.safety, default_safety), safety,
asyncness, asyncness,
constness: self.lower_constness(h.constness), constness: self.lower_constness(h.constness),
abi: self.lower_extern(h.ext), abi: self.lower_extern(h.ext),

View file

@ -250,10 +250,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
} }
} }
sym::target_feature => { sym::target_feature => {
if !tcx.is_closure_like(did.to_def_id()) let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
&& let Some(fn_sig) = fn_sig() tcx.dcx().span_delayed_bug(attr.span, "target_feature applied to non-fn");
&& fn_sig.skip_binder().safety().is_safe() 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 { if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
// The `#[target_feature]` attribute is allowed on // The `#[target_feature]` attribute is allowed on
// WebAssembly targets on all functions, including safe // 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)] #[derive(Copy, Clone, Debug, HashStable_Generic)]
pub struct FnHeader { pub struct FnHeader {
pub safety: Safety, pub safety: HeaderSafety,
pub constness: Constness, pub constness: Constness,
pub asyncness: IsAsync, pub asyncness: IsAsync,
pub abi: ExternAbi, pub abi: ExternAbi,
@ -3780,7 +3801,18 @@ impl FnHeader {
} }
pub fn is_unsafe(&self) -> bool { 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( icx.lowerer().lower_fn_ty(
hir_id, hir_id,
sig.header.safety, sig.header.safety(),
sig.header.abi, sig.header.abi,
sig.decl, sig.decl,
Some(generics), 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: _ }, _), kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _),
generics, generics,
.. ..
}) => { }) => icx.lowerer().lower_fn_ty(
icx.lowerer().lower_fn_ty(hir_id, header.safety, header.abi, decl, Some(generics), None) hir_id,
} header.safety(),
header.abi,
decl,
Some(generics),
None,
),
ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(sig, _, _), .. }) => { ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(sig, _, _), .. }) => {
let abi = tcx.hir().get_foreign_abi(hir_id); 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() => { 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.lowerer().lower_fn_ty(
icx.tcx().local_def_id_to_hir_id(def_id), icx.tcx().local_def_id_to_hir_id(def_id),
sig.header.safety, sig.header.safety(),
sig.header.abi, sig.header.abi,
sig.decl, sig.decl,
Some(generics), Some(generics),

View file

@ -2407,7 +2407,7 @@ impl<'a> State<'a> {
self.print_fn( self.print_fn(
decl, decl,
hir::FnHeader { hir::FnHeader {
safety, safety: safety.into(),
abi, abi,
constness: hir::Constness::NotConst, constness: hir::Constness::NotConst,
asyncness: hir::IsAsync::NotAsync, asyncness: hir::IsAsync::NotAsync,
@ -2423,12 +2423,20 @@ impl<'a> State<'a> {
fn print_fn_header_info(&mut self, header: hir::FnHeader) { fn print_fn_header_info(&mut self, header: hir::FnHeader) {
self.print_constness(header.constness); 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 { match header.asyncness {
hir::IsAsync::NotAsync => {} hir::IsAsync::NotAsync => {}
hir::IsAsync::Async(_) => self.word_nbsp("async"), hir::IsAsync::Async(_) => self.word_nbsp("async"),
} }
self.print_safety(header.safety); self.print_safety(safety);
if header.abi != ExternAbi::Rust { if header.abi != ExternAbi::Rust {
self.word_nbsp("extern"); self.word_nbsp("extern");

View file

@ -932,10 +932,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
return Err(TypeError::ForceInlineCast); return Err(TypeError::ForceInlineCast);
} }
// Safe `#[target_feature]` functions are not assignable to safe fn pointers let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
// (RFC 2396). 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() 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)); 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 // 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 // be correctly filled with infer. We'll use this inference to provide
// a suggestion later on. // 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 { } else {
tcx.fn_sig(def_id).instantiate_identity() tcx.fn_sig(def_id).instantiate_identity()
}; };

View file

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

View file

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

View file

@ -690,7 +690,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
if with_reduced_queries() { if with_reduced_queries() {
p!(print_def_path(def_id, args)); p!(print_def_path(def_id, args));
} else { } 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), "}}"); 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 return; // don't visit the whole expression
} }
ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => { ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
if self.thir[fun].ty.fn_sig(self.tcx).safety().is_unsafe() { let fn_ty = self.thir[fun].ty;
let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() { 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) Some(*func_id)
} else { } else {
None None
}; };
self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id)); 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, // If the called function has target features the calling function hasn't,
// the call requires `unsafe`. Don't check this on wasm // the call requires `unsafe`. Don't check this on wasm
// targets, though. For more information on wasm see the // targets, though. For more information on wasm see the
// is_like_wasm check in hir_analysis/src/collect.rs // 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 if !self.tcx.sess.target.options.is_like_wasm
&& !callee_features.iter().all(|feature| { && !callee_features.iter().all(|feature| {
self.body_target_features.iter().any(|f| f.name == feature.name) 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_id = tcx.hir().get_parent_item(hir_id);
let parent_owner = tcx.hir_owner_node(parent_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 { let unsafe_not_inherited_note = if should_suggest {
suggest_unsafe_block.then(|| { suggest_unsafe_block.then(|| {
let body_span = tcx.hir().body(parent_owner.body_id().unwrap()).value.span; let body_span = tcx.hir().body(parent_owner.body_id().unwrap()).value.span;
@ -902,7 +913,7 @@ impl UnsafeOpKind {
{ {
true true
} else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id) } 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 true
} else { } 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 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| { 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 body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features;
let mut warnings = Vec::new(); let mut warnings = Vec::new();

View file

@ -5019,12 +5019,13 @@ struct ItemInfoCollector<'a, 'ra, 'tcx> {
} }
impl ItemInfoCollector<'_, '_, '_> { 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 { let sig = DelegationFnSig {
header: sig.header, header: sig.header,
param_count: sig.decl.inputs.len(), param_count: sig.decl.inputs.len(),
has_self: sig.decl.has_self(), has_self: sig.decl.has_self(),
c_variadic: sig.decl.c_variadic(), 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); 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::Trait(box Trait { ref generics, .. })
| ItemKind::TraitAlias(ref generics, _) => { | ItemKind::TraitAlias(ref generics, _) => {
if let ItemKind::Fn(box Fn { ref sig, .. }) = &item.kind { 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); 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) { fn visit_assoc_item(&mut self, item: &'ast AssocItem, ctxt: AssocCtxt) {
if let AssocItemKind::Fn(box Fn { ref sig, .. }) = &item.kind { 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); visit::walk_assoc_item(self, item, ctxt);
} }

View file

@ -824,9 +824,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn cmp_fn_sig( fn cmp_fn_sig(
&self, &self,
sig1: &ty::PolyFnSig<'tcx>, 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>, sig2: &ty::PolyFnSig<'tcx>,
fn_def2: Option<(DefId, &'tcx [ty::GenericArg<'tcx>])>, fn_def2: Option<(DefId, Option<&'tcx [ty::GenericArg<'tcx>]>)>,
) -> (DiagStyledString, DiagStyledString) { ) -> (DiagStyledString, DiagStyledString) {
let sig1 = &(self.normalize_fn_sig)(*sig1); let sig1 = &(self.normalize_fn_sig)(*sig1);
let sig2 = &(self.normalize_fn_sig)(*sig2); 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 // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
// ^^^^^^ // ^^^^^^
values.0.push(sig1.safety.prefix_str(), sig1.safety != sig2.safety); let safety = |fn_def, sig: ty::FnSig<'_>| match fn_def {
values.1.push(sig2.safety.prefix_str(), sig1.safety != sig2.safety); 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 // 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); (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) { match (fn_def1, fn_def2) {
(None, None) => {} (Some((fn_def1, Some(fn_args1))), Some((fn_def2, Some(fn_args2)))) => {
(Some(fn_def1), Some(fn_def2)) => { let path1 = fmt(fn_def1, fn_args1);
let path1 = fmt(fn_def1); let path2 = fmt(fn_def2, fn_args2);
let path2 = fmt(fn_def2);
let same_path = path1 == path2; let same_path = path1 == path2;
values.0.push(path1, !same_path); values.0.push(path1, !same_path);
values.1.push(path2, !same_path); values.1.push(path2, !same_path);
} }
(Some(fn_def1), None) => { (Some((fn_def1, Some(fn_args1))), None) => {
values.0.push_highlighted(fmt(fn_def1)); values.0.push_highlighted(fmt(fn_def1, fn_args1));
} }
(None, Some(fn_def2)) => { (None, Some((fn_def2, Some(fn_args2)))) => {
values.1.push_highlighted(fmt(fn_def2)); values.1.push_highlighted(fmt(fn_def2, fn_args2));
} }
_ => {}
} }
values values
@ -1339,17 +1351,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
(ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => { (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1); let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1);
let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2); 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)) => { (ty::FnDef(did1, args1), ty::FnPtr(sig_tys2, hdr2)) => {
let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1); 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)) => { (ty::FnPtr(sig_tys1, hdr1), ty::FnDef(did2, args2)) => {
let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, 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)) => { (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")) (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. // Derived error. Cancel the emitter.
// NOTE(eddyb) this was `.cancel()`, but `diag` // NOTE(eddyb) this was `.cancel()`, but `diag`
// is borrowed, so we can't fully defuse it. // is borrowed, so we can't fully defuse it.
@ -1956,7 +1973,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}) })
| ObligationCauseCode::BlockTailExpression(.., source)) = code | ObligationCauseCode::BlockTailExpression(.., source)) = code
&& let hir::MatchSource::TryDesugar(_) = source && 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 { suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert {
found: found_ty.content(), found: found_ty.content(),
@ -2085,6 +2102,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn values_str( fn values_str(
&self, &self,
values: ValuePairs<'tcx>, values: ValuePairs<'tcx>,
cause: &ObligationCause<'tcx>,
) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> { ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> {
match values { match values {
ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found), 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() { if exp_found.references_error() {
return None; 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)) Some((exp, fnd, None))
} }
} }

View file

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

View file

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

View file

@ -205,9 +205,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
if self_ty.is_fn() { if self_ty.is_fn() {
let fn_sig = self_ty.fn_sig(self.tcx); let fn_sig = self_ty.fn_sig(self.tcx);
let shortname = match fn_sig.safety() { let shortname = if let ty::FnDef(def_id, _) = self_ty.kind()
hir::Safety::Safe => "fn", && self.tcx.codegen_fn_attrs(def_id).safe_target_features
hir::Safety::Unsafe => "unsafe fn", {
"#[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()))); 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 { let kind = match item.kind {
hir::ForeignItemKind::Fn(sig, names, generics) => ForeignFunctionItem( hir::ForeignItemKind::Fn(sig, names, generics) => ForeignFunctionItem(
clean_function(cx, &sig, generics, FunctionArgs::Names(names)), clean_function(cx, &sig, generics, FunctionArgs::Names(names)),
sig.header.safety, sig.header.safety(),
), ),
hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem( hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem(
Static { type_: Box::new(clean_ty(ty, cx)), mutability, expr: None }, 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::Yes => hir::IsAsync::Async(DUMMY_SP),
ty::Asyncness::No => hir::IsAsync::NotAsync, 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 { let header = match self.kind {
ItemKind::ForeignFunctionItem(_, safety) => { ItemKind::ForeignFunctionItem(_, safety) => {
let def_id = self.def_id().unwrap(); let def_id = self.def_id().unwrap();
let abi = tcx.fn_sig(def_id).skip_binder().abi(); let abi = tcx.fn_sig(def_id).skip_binder().abi();
hir::FnHeader { hir::FnHeader {
safety: if abi == ExternAbi::RustIntrinsic { safety: if tcx.codegen_fn_attrs(def_id).safe_target_features {
intrinsic_operation_unsafety(tcx, def_id.expect_local()) hir::HeaderSafety::SafeTargetFeatures
} else if abi == ExternAbi::RustIntrinsic {
intrinsic_operation_unsafety(tcx, def_id.expect_local()).into()
} else { } else {
safety safety.into()
}, },
abi, abi,
constness: if tcx.is_const_fn(def_id) { 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 { impl PrintWithSpace for hir::IsAsync {
fn print_with_space(&self) -> &str { fn print_with_space(&self) -> &str {
match self { 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 { let unsafety_flag = match myitem.kind {
clean::FunctionItem(_) | clean::ForeignFunctionItem(..) 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>" "<sup title=\"unsafe function\">⚠</sup>"
} }

View file

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

View file

@ -32,7 +32,7 @@ pub fn check(
} }
let span = cx.tcx.def_span(owner_id); 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( (false, Safety::Unsafe) => span_lint(
cx, cx,
MISSING_SAFETY_DOC, MISSING_SAFETY_DOC,

View file

@ -34,7 +34,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
ImplicitSelfKind::None => return, 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) name.strip_suffix("_unchecked").unwrap_or(name)
} else { } else {
name name

View file

@ -20,8 +20,8 @@ pub(super) fn check_fn<'tcx>(
def_id: LocalDefId, def_id: LocalDefId,
) { ) {
let safety = match kind { let safety = match kind {
intravisit::FnKind::ItemFn(_, _, hir::FnHeader { safety, .. }) => safety, intravisit::FnKind::ItemFn(_, _, header) => header.safety(),
intravisit::FnKind::Method(_, sig) => sig.header.safety, intravisit::FnKind::Method(_, sig) => sig.header.safety(),
intravisit::FnKind::Closure => return, 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<'_>) { 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 { if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind {
let body = cx.tcx.hir().body(eid); 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 if let ImplItemKind::Fn(ref signature, _) = impl_item.kind
// #11201 // #11201
&& let header = signature.header && let header = signature.header
&& header.safety.is_safe() && header.is_safe()
&& header.abi == Abi::Rust && header.abi == Abi::Rust
&& impl_item.ident.name == sym::to_string && impl_item.ident.name == sym::to_string
&& let decl = signature.decl && 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 { const FN_HEADER: hir::FnHeader = hir::FnHeader {
safety: hir::Safety::Safe, safety: hir::HeaderSafety::Normal(hir::Safety::Safe),
constness: hir::Constness::NotConst, constness: hir::Constness::NotConst,
asyncness: hir::IsAsync::NotAsync, asyncness: hir::IsAsync::NotAsync,
abi: rustc_target::spec::abi::Abi::Rust, 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 { if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
let name = impl_item.ident.name; let name = impl_item.ident.name;
let id = impl_item.owner_id; let id = impl_item.owner_id;
if sig.header.safety.is_unsafe() { if sig.header.is_unsafe() {
// can't be implemented for unsafe new // can't be implemented for unsafe new
return; return;
} }

View file

@ -541,7 +541,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio
.collect(); .collect();
if let Some(args) = args if let Some(args) = args
&& !args.is_empty() && !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( span_lint_and_then(
cx, 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 --> $DIR/fn-exception-target-features.rs:16:10
| |
LL | test(target_feature); 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 | 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` note: required by a bound in `test`
--> $DIR/fn-exception-target-features.rs:13:17 --> $DIR/fn-exception-target-features.rs:13:17
| |

View file

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

View file

@ -7,14 +7,8 @@ LL | #[target_feature(enable = "")]
LL | |_| (), LL | |_| (),
| ------ not a function definition | ------ 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 error[E0658]: `#[track_caller]` on closures is currently unstable
--> $DIR/issue-68060.rs:8:13 --> $DIR/issue-68060.rs:6:13
| |
LL | #[track_caller] LL | #[track_caller]
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@ -23,6 +17,6 @@ LL | #[track_caller]
= help: add `#![feature(closure_track_caller)]` to the crate attributes to enable = 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 = 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`. 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 | expected due to this
| |
= note: expected fn pointer `fn()` = note: expected fn pointer `fn()`
found fn item `fn() {foo}` found fn item `#[target_features] fn() {foo}`
= note: fn items are distinct from fn pointers
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers = 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 error: aborting due to 1 previous error

View file

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

View file

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

View file

@ -7,8 +7,22 @@ LL |
LL | fn foo(&self) {} LL | fn foo(&self) {}
| ------------- not an `unsafe` function | ------------- 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 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")] LL | #[target_feature(enable = "sse2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method
@ -16,5 +30,6 @@ LL |
LL | fn foo(&self) {} LL | fn foo(&self) {}
| ------------- not an `unsafe` function | ------------- 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 { trait Quux {
fn foo(); //~ NOTE `foo` from trait fn foo(); //~ NOTE `foo` from trait
//~^ NOTE: type in trait
} }
impl Quux for Foo { 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 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
fn foo() {} fn foo() {}
//~^ NOTE not an `unsafe` function //~^ NOTE not an `unsafe` function
//~| ERROR: incompatible type for trait
//~| NOTE: expected safe fn, found unsafe fn
//~| NOTE: expected signature `fn()`
} }
fn main() { fn main() {

View file

@ -126,7 +126,7 @@ LL | impl Foo {}
| ----------- not a function definition | ----------- not a function definition
error: attribute should be applied to 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")] LL | #[target_feature(enable = "sse2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -138,7 +138,7 @@ LL | | }
| |_____- not a function definition | |_____- not a function definition
error: attribute should be applied to 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")] LL | #[target_feature(enable = "sse2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -193,7 +193,7 @@ LL | fn foo();
| --------- `foo` from trait | --------- `foo` from trait
error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions 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")] LL | #[target_feature(enable = "sse2")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -205,7 +205,21 @@ LL | fn foo() {}
= help: add `#![feature(target_feature_11)]` to the crate attributes to enable = 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 = 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`. For more information about an error, try `rustc --explain E0046`.