diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b6e96b467e35..b29b3a418038e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -301,7 +301,7 @@ jobs: - name: dist-x86_64-apple env: SCRIPT: "./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin" - RUST_CONFIGURE_ARGS: "--enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false --set rust.lto=thin" + RUST_CONFIGURE_ARGS: "--enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false" RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.7 SELECT_XCODE: /Applications/Xcode_13.4.1.app diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 3946aab646a1a..e86e807279d6f 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -31,6 +31,44 @@ impl<'hir> LoweringContext<'_, 'hir> { pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> { ensure_sufficient_stack(|| { + match &e.kind { + // Paranthesis expression does not have a HirId and is handled specially. + ExprKind::Paren(ex) => { + let mut ex = self.lower_expr_mut(ex); + // Include parens in span, but only if it is a super-span. + if e.span.contains(ex.span) { + ex.span = self.lower_span(e.span); + } + // Merge attributes into the inner expression. + if !e.attrs.is_empty() { + let old_attrs = + self.attrs.get(&ex.hir_id.local_id).map(|la| *la).unwrap_or(&[]); + self.attrs.insert( + ex.hir_id.local_id, + &*self.arena.alloc_from_iter( + e.attrs + .iter() + .map(|a| self.lower_attr(a)) + .chain(old_attrs.iter().cloned()), + ), + ); + } + return ex; + } + // Desugar `ExprForLoop` + // from: `[opt_ident]: for in ` + // + // This also needs special handling because the HirId of the returned `hir::Expr` will not + // correspond to the `e.id`, so `lower_expr_for` handles attribute lowering itself. + ExprKind::ForLoop(pat, head, body, opt_label) => { + return self.lower_expr_for(e, pat, head, body, *opt_label); + } + _ => (), + } + + let hir_id = self.lower_node_id(e.id); + self.lower_attrs(hir_id, &e.attrs); + let kind = match &e.kind { ExprKind::Box(inner) => hir::ExprKind::Box(self.lower_expr(inner)), ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), @@ -48,7 +86,6 @@ impl<'hir> LoweringContext<'_, 'hir> { if e.attrs.get(0).map_or(false, |a| a.has_name(sym::rustc_box)) { if let [inner] = &args[..] && e.attrs.len() == 1 { let kind = hir::ExprKind::Box(self.lower_expr(&inner)); - let hir_id = self.lower_node_id(e.id); return hir::Expr { hir_id, kind, span: self.lower_span(e.span) }; } else { self.tcx.sess.emit_err(RustcBoxAttributeError { span: e.span }); @@ -147,7 +184,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ), ExprKind::Async(capture_clause, closure_node_id, block) => self.make_async_expr( *capture_clause, - None, + hir_id, *closure_node_id, None, e.span, @@ -184,6 +221,7 @@ impl<'hir> LoweringContext<'_, 'hir> { binder, *capture_clause, e.id, + hir_id, *closure_id, fn_decl, body, @@ -279,39 +317,12 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), ExprKind::Err => hir::ExprKind::Err, ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr), - ExprKind::Paren(ex) => { - let mut ex = self.lower_expr_mut(ex); - // Include parens in span, but only if it is a super-span. - if e.span.contains(ex.span) { - ex.span = self.lower_span(e.span); - } - // Merge attributes into the inner expression. - if !e.attrs.is_empty() { - let old_attrs = - self.attrs.get(&ex.hir_id.local_id).map(|la| *la).unwrap_or(&[]); - self.attrs.insert( - ex.hir_id.local_id, - &*self.arena.alloc_from_iter( - e.attrs - .iter() - .map(|a| self.lower_attr(a)) - .chain(old_attrs.iter().cloned()), - ), - ); - } - return ex; - } - // Desugar `ExprForLoop` - // from: `[opt_ident]: for in ` - ExprKind::ForLoop(pat, head, body, opt_label) => { - return self.lower_expr_for(e, pat, head, body, *opt_label); - } + ExprKind::Paren(_) | ExprKind::ForLoop(..) => unreachable!("already handled"), + ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), }; - let hir_id = self.lower_node_id(e.id); - self.lower_attrs(hir_id, &e.attrs); hir::Expr { hir_id, kind, span: self.lower_span(e.span) } }) } @@ -576,7 +587,7 @@ impl<'hir> LoweringContext<'_, 'hir> { pub(super) fn make_async_expr( &mut self, capture_clause: CaptureBy, - outer_hir_id: Option, + outer_hir_id: hir::HirId, closure_node_id: NodeId, ret_ty: Option>, span: Span, @@ -669,8 +680,9 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Closure(c) }; - let track_caller = outer_hir_id - .and_then(|id| self.attrs.get(&id.local_id)) + let track_caller = self + .attrs + .get(&outer_hir_id.local_id) .map_or(false, |attrs| attrs.into_iter().any(|attr| attr.has_name(sym::track_caller))); let hir_id = self.lower_node_id(closure_node_id); @@ -985,6 +997,7 @@ impl<'hir> LoweringContext<'_, 'hir> { binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, + closure_hir_id: hir::HirId, inner_closure_id: NodeId, decl: &FnDecl, body: &Expr, @@ -1018,9 +1031,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let async_body = this.make_async_expr( capture_clause, - // FIXME(nbdd0121): This should also use a proper HIR id so `#[track_caller]` - // can be applied on async closures as well. - None, + closure_hir_id, inner_closure_id, async_ret_ty, body.span, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index d73d6d3918ea6..73065ab516350 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1139,7 +1139,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let async_expr = this.make_async_expr( CaptureBy::Value, - Some(fn_id), + fn_id, closure_id, None, body.span, diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs new file mode 100644 index 0000000000000..c7f2e1966c1ed --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -0,0 +1,688 @@ +use rustc_ast::{ast, MetaItemKind, NestedMetaItem}; +use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; +use rustc_hir::{lang_items, weak_lang_items::WEAK_LANG_ITEMS, LangItem}; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; +use rustc_middle::mir::mono::Linkage; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self as ty, DefIdTree, TyCtxt}; +use rustc_session::{lint, parse::feature_err}; +use rustc_span::{sym, Span}; +use rustc_target::spec::{abi, SanitizerSet}; + +use crate::target_features::from_target_feature; +use crate::{errors::ExpectedUsedSymbol, target_features::check_target_feature_trait_unsafe}; + +fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage { + use rustc_middle::mir::mono::Linkage::*; + + // Use the names from src/llvm/docs/LangRef.rst here. Most types are only + // applicable to variable declarations and may not really make sense for + // Rust code in the first place but allow them anyway and trust that the + // user knows what they're doing. Who knows, unanticipated use cases may pop + // up in the future. + // + // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported + // and don't have to be, LLVM treats them as no-ops. + match name { + "appending" => Appending, + "available_externally" => AvailableExternally, + "common" => Common, + "extern_weak" => ExternalWeak, + "external" => External, + "internal" => Internal, + "linkonce" => LinkOnceAny, + "linkonce_odr" => LinkOnceODR, + "private" => Private, + "weak" => WeakAny, + "weak_odr" => WeakODR, + _ => tcx.sess.span_fatal(tcx.def_span(def_id), "invalid linkage specified"), + } +} + +fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { + if cfg!(debug_assertions) { + let def_kind = tcx.def_kind(did); + assert!( + def_kind.has_codegen_attrs(), + "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}", + ); + } + + let did = did.expect_local(); + let attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(did)); + let mut codegen_fn_attrs = CodegenFnAttrs::new(); + if tcx.should_inherit_track_caller(did) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; + } + + let supported_target_features = tcx.supported_target_features(LOCAL_CRATE); + + let mut inline_span = None; + let mut link_ordinal_span = None; + let mut no_sanitize_span = None; + for attr in attrs.iter() { + if attr.has_name(sym::cold) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD; + } else if attr.has_name(sym::rustc_allocator) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR; + } else if attr.has_name(sym::ffi_returns_twice) { + if tcx.is_foreign_item(did) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE; + } else { + // `#[ffi_returns_twice]` is only allowed `extern fn`s. + struct_span_err!( + tcx.sess, + attr.span, + E0724, + "`#[ffi_returns_twice]` may only be used on foreign functions" + ) + .emit(); + } + } else if attr.has_name(sym::ffi_pure) { + if tcx.is_foreign_item(did) { + if attrs.iter().any(|a| a.has_name(sym::ffi_const)) { + // `#[ffi_const]` functions cannot be `#[ffi_pure]` + struct_span_err!( + tcx.sess, + attr.span, + E0757, + "`#[ffi_const]` function cannot be `#[ffi_pure]`" + ) + .emit(); + } else { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE; + } + } else { + // `#[ffi_pure]` is only allowed on foreign functions + struct_span_err!( + tcx.sess, + attr.span, + E0755, + "`#[ffi_pure]` may only be used on foreign functions" + ) + .emit(); + } + } else if attr.has_name(sym::ffi_const) { + if tcx.is_foreign_item(did) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST; + } else { + // `#[ffi_const]` is only allowed on foreign functions + struct_span_err!( + tcx.sess, + attr.span, + E0756, + "`#[ffi_const]` may only be used on foreign functions" + ) + .emit(); + } + } else if attr.has_name(sym::rustc_nounwind) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; + } else if attr.has_name(sym::rustc_reallocator) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR; + } else if attr.has_name(sym::rustc_deallocator) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR; + } else if attr.has_name(sym::rustc_allocator_zeroed) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED; + } else if attr.has_name(sym::naked) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED; + } else if attr.has_name(sym::no_mangle) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + } else if attr.has_name(sym::no_coverage) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE; + } else if attr.has_name(sym::rustc_std_internal_symbol) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + } else if attr.has_name(sym::used) { + let inner = attr.meta_item_list(); + match inner.as_deref() { + Some([item]) if item.has_name(sym::linker) => { + if !tcx.features().used_with_arg { + feature_err( + &tcx.sess.parse_sess, + sym::used_with_arg, + attr.span, + "`#[used(linker)]` is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER; + } + Some([item]) if item.has_name(sym::compiler) => { + if !tcx.features().used_with_arg { + feature_err( + &tcx.sess.parse_sess, + sym::used_with_arg, + attr.span, + "`#[used(compiler)]` is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; + } + Some(_) => { + tcx.sess.emit_err(ExpectedUsedSymbol { span: attr.span }); + } + None => { + // Unfortunately, unconditionally using `llvm.used` causes + // issues in handling `.init_array` with the gold linker, + // but using `llvm.compiler.used` caused a nontrival amount + // of unintentional ecosystem breakage -- particularly on + // Mach-O targets. + // + // As a result, we emit `llvm.compiler.used` only on ELF + // targets. This is somewhat ad-hoc, but actually follows + // our pre-LLVM 13 behavior (prior to the ecosystem + // breakage), and seems to match `clang`'s behavior as well + // (both before and after LLVM 13), possibly because they + // have similar compatibility concerns to us. See + // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146 + // and following comments for some discussion of this, as + // well as the comments in `rustc_codegen_llvm` where these + // flags are handled. + // + // Anyway, to be clear: this is still up in the air + // somewhat, and is subject to change in the future (which + // is a good thing, because this would ideally be a bit + // more firmed up). + let is_like_elf = !(tcx.sess.target.is_like_osx + || tcx.sess.target.is_like_windows + || tcx.sess.target.is_like_wasm); + codegen_fn_attrs.flags |= if is_like_elf { + CodegenFnAttrFlags::USED + } else { + CodegenFnAttrFlags::USED_LINKER + }; + } + } + } else if attr.has_name(sym::cmse_nonsecure_entry) { + if !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. }) { + struct_span_err!( + tcx.sess, + attr.span, + E0776, + "`#[cmse_nonsecure_entry]` requires C ABI" + ) + .emit(); + } + if !tcx.sess.target.llvm_target.contains("thumbv8m") { + struct_span_err!(tcx.sess, attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension") + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY; + } else if attr.has_name(sym::thread_local) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL; + } else if attr.has_name(sym::track_caller) { + if !tcx.is_closure(did.to_def_id()) && tcx.fn_sig(did).abi() != abi::Abi::Rust { + struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI") + .emit(); + } + if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller { + feature_err( + &tcx.sess.parse_sess, + sym::closure_track_caller, + attr.span, + "`#[track_caller]` on closures is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; + } else if attr.has_name(sym::export_name) { + if let Some(s) = attr.value_str() { + if s.as_str().contains('\0') { + // `#[export_name = ...]` will be converted to a null-terminated string, + // so it may not contain any null characters. + struct_span_err!( + tcx.sess, + attr.span, + E0648, + "`export_name` may not contain null characters" + ) + .emit(); + } + codegen_fn_attrs.export_name = Some(s); + } + } else if attr.has_name(sym::target_feature) { + if !tcx.is_closure(did.to_def_id()) + && tcx.fn_sig(did).unsafety() == hir::Unsafety::Normal + { + 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 + // ones. Other targets require that `#[target_feature]` is + // only applied to unsafe functions (pending the + // `target_feature_11` feature) because on most targets + // execution of instructions that are not supported is + // considered undefined behavior. For WebAssembly which is a + // 100% safe target at execution time it's not possible to + // execute undefined instructions, and even if a future + // feature was added in some form for this it would be a + // deterministic trap. There is no undefined behavior when + // executing WebAssembly so `#[target_feature]` is allowed + // on safe functions (but again, only for WebAssembly) + // + // Note that this is also allowed if `actually_rustdoc` so + // if a target is documenting some wasm-specific code then + // it's not spuriously denied. + } else if !tcx.features().target_feature_11 { + let mut err = feature_err( + &tcx.sess.parse_sess, + sym::target_feature_11, + attr.span, + "`#[target_feature(..)]` can only be applied to `unsafe` functions", + ); + err.span_label(tcx.def_span(did), "not an `unsafe` function"); + err.emit(); + } else { + check_target_feature_trait_unsafe(tcx, did, attr.span); + } + } + from_target_feature( + tcx, + attr, + supported_target_features, + &mut codegen_fn_attrs.target_features, + ); + } else if attr.has_name(sym::linkage) { + if let Some(val) = attr.value_str() { + let linkage = Some(linkage_by_name(tcx, did, val.as_str())); + if tcx.is_foreign_item(did) { + codegen_fn_attrs.import_linkage = linkage; + } else { + codegen_fn_attrs.linkage = linkage; + } + } + } else if attr.has_name(sym::link_section) { + if let Some(val) = attr.value_str() { + if val.as_str().bytes().any(|b| b == 0) { + let msg = format!( + "illegal null byte in link_section \ + value: `{}`", + &val + ); + tcx.sess.span_err(attr.span, &msg); + } else { + codegen_fn_attrs.link_section = Some(val); + } + } + } else if attr.has_name(sym::link_name) { + codegen_fn_attrs.link_name = attr.value_str(); + } else if attr.has_name(sym::link_ordinal) { + link_ordinal_span = Some(attr.span); + if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { + codegen_fn_attrs.link_ordinal = ordinal; + } + } else if attr.has_name(sym::no_sanitize) { + no_sanitize_span = Some(attr.span); + if let Some(list) = attr.meta_item_list() { + for item in list.iter() { + if item.has_name(sym::address) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS; + } else if item.has_name(sym::cfi) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI; + } else if item.has_name(sym::kcfi) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI; + } else if item.has_name(sym::memory) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; + } else if item.has_name(sym::memtag) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG; + } else if item.has_name(sym::shadow_call_stack) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK; + } else if item.has_name(sym::thread) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; + } else if item.has_name(sym::hwaddress) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS; + } else { + tcx.sess + .struct_span_err(item.span(), "invalid argument for `no_sanitize`") + .note("expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`") + .emit(); + } + } + } + } else if attr.has_name(sym::instruction_set) { + codegen_fn_attrs.instruction_set = match attr.meta_kind() { + Some(MetaItemKind::List(ref items)) => match items.as_slice() { + [NestedMetaItem::MetaItem(set)] => { + let segments = + set.path.segments.iter().map(|x| x.ident.name).collect::>(); + match segments.as_slice() { + [sym::arm, sym::a32] | [sym::arm, sym::t32] => { + if !tcx.sess.target.has_thumb_interworking { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0779, + "target does not support `#[instruction_set]`" + ) + .emit(); + None + } else if segments[1] == sym::a32 { + Some(InstructionSetAttr::ArmA32) + } else if segments[1] == sym::t32 { + Some(InstructionSetAttr::ArmT32) + } else { + unreachable!() + } + } + _ => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0779, + "invalid instruction set specified", + ) + .emit(); + None + } + } + } + [] => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0778, + "`#[instruction_set]` requires an argument" + ) + .emit(); + None + } + _ => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0779, + "cannot specify more than one instruction set" + ) + .emit(); + None + } + }, + _ => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0778, + "must specify an instruction set" + ) + .emit(); + None + } + }; + } else if attr.has_name(sym::repr) { + codegen_fn_attrs.alignment = match attr.meta_item_list() { + Some(items) => match items.as_slice() { + [item] => match item.name_value_literal() { + Some((sym::align, literal)) => { + let alignment = rustc_attr::parse_alignment(&literal.kind); + + match alignment { + Ok(align) => Some(align), + Err(msg) => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0589, + "invalid `repr(align)` attribute: {}", + msg + ) + .emit(); + + None + } + } + } + _ => None, + }, + [] => None, + _ => None, + }, + None => None, + }; + } + } + + codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { + if !attr.has_name(sym::inline) { + return ia; + } + match attr.meta_kind() { + Some(MetaItemKind::Word) => InlineAttr::Hint, + Some(MetaItemKind::List(ref items)) => { + inline_span = Some(attr.span); + if items.len() != 1 { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0534, + "expected one argument" + ) + .emit(); + InlineAttr::None + } else if list_contains_name(&items, sym::always) { + InlineAttr::Always + } else if list_contains_name(&items, sym::never) { + InlineAttr::Never + } else { + struct_span_err!( + tcx.sess.diagnostic(), + items[0].span(), + E0535, + "invalid argument" + ) + .help("valid inline arguments are `always` and `never`") + .emit(); + + InlineAttr::None + } + } + Some(MetaItemKind::NameValue(_)) => ia, + None => ia, + } + }); + + codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| { + if !attr.has_name(sym::optimize) { + return ia; + } + let err = |sp, s| struct_span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s).emit(); + match attr.meta_kind() { + Some(MetaItemKind::Word) => { + err(attr.span, "expected one argument"); + ia + } + Some(MetaItemKind::List(ref items)) => { + inline_span = Some(attr.span); + if items.len() != 1 { + err(attr.span, "expected one argument"); + OptimizeAttr::None + } else if list_contains_name(&items, sym::size) { + OptimizeAttr::Size + } else if list_contains_name(&items, sym::speed) { + OptimizeAttr::Speed + } else { + err(items[0].span(), "invalid argument"); + OptimizeAttr::None + } + } + Some(MetaItemKind::NameValue(_)) => ia, + None => ia, + } + }); + + // #73631: closures inherit `#[target_feature]` annotations + if tcx.features().target_feature_11 && tcx.is_closure(did.to_def_id()) { + let owner_id = tcx.parent(did.to_def_id()); + if tcx.def_kind(owner_id).has_codegen_attrs() { + codegen_fn_attrs + .target_features + .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied()); + } + } + + // If a function uses #[target_feature] it can't be inlined into general + // purpose functions as they wouldn't have the right target features + // enabled. For that reason we also forbid #[inline(always)] as it can't be + // respected. + if !codegen_fn_attrs.target_features.is_empty() { + if codegen_fn_attrs.inline == InlineAttr::Always { + if let Some(span) = inline_span { + tcx.sess.span_err( + span, + "cannot use `#[inline(always)]` with \ + `#[target_feature]`", + ); + } + } + } + + if !codegen_fn_attrs.no_sanitize.is_empty() { + if codegen_fn_attrs.inline == InlineAttr::Always { + if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) { + let hir_id = tcx.hir().local_def_id_to_hir_id(did); + tcx.struct_span_lint_hir( + lint::builtin::INLINE_NO_SANITIZE, + hir_id, + no_sanitize_span, + "`no_sanitize` will have no effect after inlining", + |lint| lint.span_note(inline_span, "inlining requested here"), + ) + } + } + } + + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE; + codegen_fn_attrs.inline = InlineAttr::Never; + } + + // Weak lang items have the same semantics as "std internal" symbols in the + // sense that they're preserved through all our LTO passes and only + // strippable by the linker. + // + // Additionally weak lang items have predetermined symbol names. + if WEAK_LANG_ITEMS.iter().any(|&l| tcx.lang_items().get(l) == Some(did.to_def_id())) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + } + if let Some((name, _)) = lang_items::extract(attrs) + && let Some(lang_item) = LangItem::from_name(name) + && let Some(link_name) = lang_item.link_name() + { + codegen_fn_attrs.export_name = Some(link_name); + codegen_fn_attrs.link_name = Some(link_name); + } + check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span); + + // Internal symbols to the standard library all have no_mangle semantics in + // that they have defined symbol names present in the function name. This + // also applies to weak symbols where they all have known symbol names. + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + } + + // Any linkage to LLVM intrinsics for now forcibly marks them all as never + // unwinds since LLVM sometimes can't handle codegen which `invoke`s + // intrinsic functions. + if let Some(name) = &codegen_fn_attrs.link_name { + if name.as_str().starts_with("llvm.") { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; + } + } + + codegen_fn_attrs +} + +/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller +/// applied to the method prototype. +fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if let Some(impl_item) = tcx.opt_associated_item(def_id) + && let ty::AssocItemContainer::ImplContainer = impl_item.container + && let Some(trait_item) = impl_item.trait_item_def_id + { + return tcx + .codegen_fn_attrs(trait_item) + .flags + .intersects(CodegenFnAttrFlags::TRACK_CALLER); + } + + false +} + +fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { + use rustc_ast::{LitIntType, LitKind, MetaItemLit}; + if !tcx.features().raw_dylib && tcx.sess.target.arch == "x86" { + feature_err( + &tcx.sess.parse_sess, + sym::raw_dylib, + attr.span, + "`#[link_ordinal]` is unstable on x86", + ) + .emit(); + } + let meta_item_list = attr.meta_item_list(); + let meta_item_list = meta_item_list.as_deref(); + let sole_meta_list = match meta_item_list { + Some([item]) => item.lit(), + Some(_) => { + tcx.sess + .struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`") + .note("the attribute requires exactly one argument") + .emit(); + return None; + } + _ => None, + }; + if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = + sole_meta_list + { + // According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, + // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined + // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information + // to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t. + // + // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this: + // both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies + // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library + // for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import + // library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet + // if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment + // about LINK.EXE failing.) + if *ordinal <= u16::MAX as u128 { + Some(*ordinal as u16) + } else { + let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal); + tcx.sess + .struct_span_err(attr.span, &msg) + .note("the value may not exceed `u16::MAX`") + .emit(); + None + } + } else { + tcx.sess + .struct_span_err(attr.span, "illegal ordinal format in `link_ordinal`") + .note("an unsuffixed integer value, e.g., `1`, is expected") + .emit(); + None + } +} + +fn check_link_name_xor_ordinal( + tcx: TyCtxt<'_>, + codegen_fn_attrs: &CodegenFnAttrs, + inline_span: Option, +) { + if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() { + return; + } + let msg = "cannot use `#[link_name]` with `#[link_ordinal]`"; + if let Some(span) = inline_span { + tcx.sess.span_err(span, msg); + } else { + tcx.sess.err(msg); + } +} + +pub fn provide(providers: &mut Providers) { + *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers }; +} diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index e3b6fbf1bc7f0..0620000201f02 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -548,3 +548,10 @@ pub struct ArchiveBuildFailure { pub struct UnknownArchiveKind<'a> { pub kind: &'a str, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa_expected_used_symbol)] +pub struct ExpectedUsedSymbol { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index def6390f6a36b..0e6596d4ba781 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -42,6 +42,7 @@ use std::path::{Path, PathBuf}; pub mod back; pub mod base; +pub mod codegen_attrs; pub mod common; pub mod coverageinfo; pub mod debuginfo; @@ -180,6 +181,7 @@ pub fn provide(providers: &mut Providers) { crate::back::symbol_export::provide(providers); crate::base::provide(providers); crate::target_features::provide(providers); + crate::codegen_attrs::provide(providers); } pub fn provide_extern(providers: &mut ExternProviders) { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 301683e8e8544..0dabe96b60277 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,8 +1,19 @@ +use rustc_ast::ast; +use rustc_attr::InstructionSetAttr; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::symbol::Symbol; +use rustc_span::Span; /// Features that control behaviour of rustc, rather than the codegen. pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; @@ -322,15 +333,148 @@ pub fn tied_target_features(sess: &Session) -> &'static [&'static [&'static str] } } -pub(crate) fn provide(providers: &mut Providers) { - providers.supported_target_features = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - if tcx.sess.opts.actually_rustdoc { - // rustdoc needs to be able to document functions that use all the features, so - // whitelist them all - all_known_features().map(|(a, b)| (a.to_string(), b)).collect() - } else { - supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect() - } +pub fn from_target_feature( + tcx: TyCtxt<'_>, + attr: &ast::Attribute, + supported_target_features: &FxHashMap>, + target_features: &mut Vec, +) { + let Some(list) = attr.meta_item_list() else { return }; + let bad_item = |span| { + let msg = "malformed `target_feature` attribute input"; + let code = "enable = \"..\""; + tcx.sess + .struct_span_err(span, msg) + .span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders) + .emit(); }; + let rust_features = tcx.features(); + for item in list { + // Only `enable = ...` is accepted in the meta-item list. + if !item.has_name(sym::enable) { + bad_item(item.span()); + continue; + } + + // Must be of the form `enable = "..."` (a string). + let Some(value) = item.value_str() else { + bad_item(item.span()); + continue; + }; + + // We allow comma separation to enable multiple features. + target_features.extend(value.as_str().split(',').filter_map(|feature| { + let Some(feature_gate) = supported_target_features.get(feature) else { + let msg = + format!("the feature named `{}` is not valid for this target", feature); + let mut err = tcx.sess.struct_span_err(item.span(), &msg); + err.span_label( + item.span(), + format!("`{}` is not valid for this target", feature), + ); + if let Some(stripped) = feature.strip_prefix('+') { + let valid = supported_target_features.contains_key(stripped); + if valid { + err.help("consider removing the leading `+` in the feature name"); + } + } + err.emit(); + return None; + }; + + // Only allow features whose feature gates have been enabled. + let allowed = match feature_gate.as_ref().copied() { + Some(sym::arm_target_feature) => rust_features.arm_target_feature, + Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature, + Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature, + Some(sym::mips_target_feature) => rust_features.mips_target_feature, + Some(sym::riscv_target_feature) => rust_features.riscv_target_feature, + Some(sym::avx512_target_feature) => rust_features.avx512_target_feature, + Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, + Some(sym::tbm_target_feature) => rust_features.tbm_target_feature, + Some(sym::wasm_target_feature) => rust_features.wasm_target_feature, + Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature, + Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, + Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, + Some(sym::f16c_target_feature) => rust_features.f16c_target_feature, + Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature, + Some(sym::bpf_target_feature) => rust_features.bpf_target_feature, + Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature, + Some(name) => bug!("unknown target feature gate {}", name), + None => true, + }; + if !allowed { + feature_err( + &tcx.sess.parse_sess, + feature_gate.unwrap(), + item.span(), + &format!("the target feature `{}` is currently unstable", feature), + ) + .emit(); + } + Some(Symbol::intern(feature)) + })); + } +} + +/// Computes the set of target features used in a function for the purposes of +/// inline assembly. +fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet { + let mut target_features = tcx.sess.unstable_target_features.clone(); + if tcx.def_kind(did).has_codegen_attrs() { + let attrs = tcx.codegen_fn_attrs(did); + target_features.extend(&attrs.target_features); + match attrs.instruction_set { + None => {} + Some(InstructionSetAttr::ArmA32) => { + target_features.remove(&sym::thumb_mode); + } + Some(InstructionSetAttr::ArmT32) => { + target_features.insert(sym::thumb_mode); + } + } + } + + tcx.arena.alloc(target_features) +} + +/// Checks the function annotated with `#[target_feature]` is not a safe +/// trait method implementation, reporting an error if it is. +pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { + let hir_id = tcx.hir().local_def_id_to_hir_id(id); + let node = tcx.hir().get(hir_id); + if let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node { + let parent_id = tcx.hir().get_parent_item(hir_id); + let parent_item = tcx.hir().expect_item(parent_id.def_id); + if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = parent_item.kind { + tcx.sess + .struct_span_err( + attr_span, + "`#[target_feature(..)]` cannot be applied to safe trait method", + ) + .span_label(attr_span, "cannot be applied to safe trait method") + .span_label(tcx.def_span(id), "not an `unsafe` function") + .emit(); + } + } +} + +pub(crate) fn provide(providers: &mut Providers) { + *providers = Providers { + supported_target_features: |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + if tcx.sess.opts.actually_rustdoc { + // rustdoc needs to be able to document functions that use all the features, so + // whitelist them all + all_known_features().map(|(a, b)| (a.to_string(), b)).collect() + } else { + supported_target_features(tcx.sess) + .iter() + .map(|&(a, b)| (a.to_string(), b)) + .collect() + } + }, + asm_target_features, + ..*providers + } } diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl index 4d1f9c1c901fc..db4c82b35c778 100644 --- a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl +++ b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl @@ -192,3 +192,5 @@ codegen_ssa_archive_build_failure = codegen_ssa_unknown_archive_kind = Don't know how to build archive of type: {$kind} + +codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl index a9ea161b93eda..e33323a779536 100644 --- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl +++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl @@ -101,8 +101,6 @@ hir_analysis_extern_crate_not_idiomatic = `extern crate` is not idiomatic in the new edition .suggestion = convert it to a `{$msg_code}` -hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` - hir_analysis_const_impl_for_non_const_trait = const `impl` for trait `{$trait_name}` which is not marked with `#[const_trait]` .suggestion = mark `{$trait_name}` as const diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 72288e5bc761f..1eeaaf55e63af 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -17,28 +17,20 @@ use crate::astconv::AstConv; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::errors; -use rustc_ast as ast; -use rustc_ast::{MetaItemKind, NestedMetaItem}; -use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey}; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; -use rustc_hir::{lang_items, GenericParamKind, LangItem, Node}; +use rustc_hir::{GenericParamKind, Node}; use rustc_middle::hir::nested_filter; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; -use rustc_middle::mir::mono::Linkage; use rustc_middle::ty::query::Providers; use rustc_middle::ty::util::{Discr, IntTypeExt}; -use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, IsSuggestable, ToPredicate, Ty, TyCtxt}; -use rustc_session::lint; -use rustc_session::parse::feature_err; +use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; -use rustc_target::spec::{abi, SanitizerSet}; +use rustc_target::spec::abi; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; use std::iter; @@ -78,10 +70,7 @@ pub fn provide(providers: &mut Providers) { impl_polarity, is_foreign_item, generator_kind, - codegen_fn_attrs, - asm_target_features, collect_mod_item_types, - should_inherit_track_caller, ..*providers }; } @@ -1455,797 +1444,3 @@ fn generator_kind(tcx: TyCtxt<'_>, def_id: DefId) -> Option _ => bug!("generator_kind applied to non-local def-id {:?}", def_id), } } - -fn from_target_feature( - tcx: TyCtxt<'_>, - attr: &ast::Attribute, - supported_target_features: &FxHashMap>, - target_features: &mut Vec, -) { - let Some(list) = attr.meta_item_list() else { return }; - let bad_item = |span| { - let msg = "malformed `target_feature` attribute input"; - let code = "enable = \"..\""; - tcx.sess - .struct_span_err(span, msg) - .span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders) - .emit(); - }; - let rust_features = tcx.features(); - for item in list { - // Only `enable = ...` is accepted in the meta-item list. - if !item.has_name(sym::enable) { - bad_item(item.span()); - continue; - } - - // Must be of the form `enable = "..."` (a string). - let Some(value) = item.value_str() else { - bad_item(item.span()); - continue; - }; - - // We allow comma separation to enable multiple features. - target_features.extend(value.as_str().split(',').filter_map(|feature| { - let Some(feature_gate) = supported_target_features.get(feature) else { - let msg = - format!("the feature named `{}` is not valid for this target", feature); - let mut err = tcx.sess.struct_span_err(item.span(), &msg); - err.span_label( - item.span(), - format!("`{}` is not valid for this target", feature), - ); - if let Some(stripped) = feature.strip_prefix('+') { - let valid = supported_target_features.contains_key(stripped); - if valid { - err.help("consider removing the leading `+` in the feature name"); - } - } - err.emit(); - return None; - }; - - // Only allow features whose feature gates have been enabled. - let allowed = match feature_gate.as_ref().copied() { - Some(sym::arm_target_feature) => rust_features.arm_target_feature, - Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature, - Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature, - Some(sym::mips_target_feature) => rust_features.mips_target_feature, - Some(sym::riscv_target_feature) => rust_features.riscv_target_feature, - Some(sym::avx512_target_feature) => rust_features.avx512_target_feature, - Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, - Some(sym::tbm_target_feature) => rust_features.tbm_target_feature, - Some(sym::wasm_target_feature) => rust_features.wasm_target_feature, - Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature, - Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, - Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, - Some(sym::f16c_target_feature) => rust_features.f16c_target_feature, - Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature, - Some(sym::bpf_target_feature) => rust_features.bpf_target_feature, - Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature, - Some(name) => bug!("unknown target feature gate {}", name), - None => true, - }; - if !allowed { - feature_err( - &tcx.sess.parse_sess, - feature_gate.unwrap(), - item.span(), - &format!("the target feature `{}` is currently unstable", feature), - ) - .emit(); - } - Some(Symbol::intern(feature)) - })); - } -} - -fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage { - use rustc_middle::mir::mono::Linkage::*; - - // Use the names from src/llvm/docs/LangRef.rst here. Most types are only - // applicable to variable declarations and may not really make sense for - // Rust code in the first place but allow them anyway and trust that the - // user knows what they're doing. Who knows, unanticipated use cases may pop - // up in the future. - // - // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported - // and don't have to be, LLVM treats them as no-ops. - match name { - "appending" => Appending, - "available_externally" => AvailableExternally, - "common" => Common, - "extern_weak" => ExternalWeak, - "external" => External, - "internal" => Internal, - "linkonce" => LinkOnceAny, - "linkonce_odr" => LinkOnceODR, - "private" => Private, - "weak" => WeakAny, - "weak_odr" => WeakODR, - _ => tcx.sess.span_fatal(tcx.def_span(def_id), "invalid linkage specified"), - } -} - -fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { - if cfg!(debug_assertions) { - let def_kind = tcx.def_kind(did); - assert!( - def_kind.has_codegen_attrs(), - "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}", - ); - } - - let did = did.expect_local(); - let attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(did)); - let mut codegen_fn_attrs = CodegenFnAttrs::new(); - if tcx.should_inherit_track_caller(did) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; - } - - let supported_target_features = tcx.supported_target_features(LOCAL_CRATE); - - let mut inline_span = None; - let mut link_ordinal_span = None; - let mut no_sanitize_span = None; - for attr in attrs.iter() { - if attr.has_name(sym::cold) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD; - } else if attr.has_name(sym::rustc_allocator) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR; - } else if attr.has_name(sym::ffi_returns_twice) { - if tcx.is_foreign_item(did) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE; - } else { - // `#[ffi_returns_twice]` is only allowed `extern fn`s. - struct_span_err!( - tcx.sess, - attr.span, - E0724, - "`#[ffi_returns_twice]` may only be used on foreign functions" - ) - .emit(); - } - } else if attr.has_name(sym::ffi_pure) { - if tcx.is_foreign_item(did) { - if attrs.iter().any(|a| a.has_name(sym::ffi_const)) { - // `#[ffi_const]` functions cannot be `#[ffi_pure]` - struct_span_err!( - tcx.sess, - attr.span, - E0757, - "`#[ffi_const]` function cannot be `#[ffi_pure]`" - ) - .emit(); - } else { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE; - } - } else { - // `#[ffi_pure]` is only allowed on foreign functions - struct_span_err!( - tcx.sess, - attr.span, - E0755, - "`#[ffi_pure]` may only be used on foreign functions" - ) - .emit(); - } - } else if attr.has_name(sym::ffi_const) { - if tcx.is_foreign_item(did) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST; - } else { - // `#[ffi_const]` is only allowed on foreign functions - struct_span_err!( - tcx.sess, - attr.span, - E0756, - "`#[ffi_const]` may only be used on foreign functions" - ) - .emit(); - } - } else if attr.has_name(sym::rustc_nounwind) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; - } else if attr.has_name(sym::rustc_reallocator) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR; - } else if attr.has_name(sym::rustc_deallocator) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR; - } else if attr.has_name(sym::rustc_allocator_zeroed) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED; - } else if attr.has_name(sym::naked) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED; - } else if attr.has_name(sym::no_mangle) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; - } else if attr.has_name(sym::no_coverage) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE; - } else if attr.has_name(sym::rustc_std_internal_symbol) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; - } else if attr.has_name(sym::used) { - let inner = attr.meta_item_list(); - match inner.as_deref() { - Some([item]) if item.has_name(sym::linker) => { - if !tcx.features().used_with_arg { - feature_err( - &tcx.sess.parse_sess, - sym::used_with_arg, - attr.span, - "`#[used(linker)]` is currently unstable", - ) - .emit(); - } - codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER; - } - Some([item]) if item.has_name(sym::compiler) => { - if !tcx.features().used_with_arg { - feature_err( - &tcx.sess.parse_sess, - sym::used_with_arg, - attr.span, - "`#[used(compiler)]` is currently unstable", - ) - .emit(); - } - codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; - } - Some(_) => { - tcx.sess.emit_err(errors::ExpectedUsedSymbol { span: attr.span }); - } - None => { - // Unfortunately, unconditionally using `llvm.used` causes - // issues in handling `.init_array` with the gold linker, - // but using `llvm.compiler.used` caused a nontrival amount - // of unintentional ecosystem breakage -- particularly on - // Mach-O targets. - // - // As a result, we emit `llvm.compiler.used` only on ELF - // targets. This is somewhat ad-hoc, but actually follows - // our pre-LLVM 13 behavior (prior to the ecosystem - // breakage), and seems to match `clang`'s behavior as well - // (both before and after LLVM 13), possibly because they - // have similar compatibility concerns to us. See - // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146 - // and following comments for some discussion of this, as - // well as the comments in `rustc_codegen_llvm` where these - // flags are handled. - // - // Anyway, to be clear: this is still up in the air - // somewhat, and is subject to change in the future (which - // is a good thing, because this would ideally be a bit - // more firmed up). - let is_like_elf = !(tcx.sess.target.is_like_osx - || tcx.sess.target.is_like_windows - || tcx.sess.target.is_like_wasm); - codegen_fn_attrs.flags |= if is_like_elf { - CodegenFnAttrFlags::USED - } else { - CodegenFnAttrFlags::USED_LINKER - }; - } - } - } else if attr.has_name(sym::cmse_nonsecure_entry) { - if !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. }) { - struct_span_err!( - tcx.sess, - attr.span, - E0776, - "`#[cmse_nonsecure_entry]` requires C ABI" - ) - .emit(); - } - if !tcx.sess.target.llvm_target.contains("thumbv8m") { - struct_span_err!(tcx.sess, attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension") - .emit(); - } - codegen_fn_attrs.flags |= CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY; - } else if attr.has_name(sym::thread_local) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL; - } else if attr.has_name(sym::track_caller) { - if !tcx.is_closure(did.to_def_id()) && tcx.fn_sig(did).abi() != abi::Abi::Rust { - struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI") - .emit(); - } - if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller { - feature_err( - &tcx.sess.parse_sess, - sym::closure_track_caller, - attr.span, - "`#[track_caller]` on closures is currently unstable", - ) - .emit(); - } - codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; - } else if attr.has_name(sym::export_name) { - if let Some(s) = attr.value_str() { - if s.as_str().contains('\0') { - // `#[export_name = ...]` will be converted to a null-terminated string, - // so it may not contain any null characters. - struct_span_err!( - tcx.sess, - attr.span, - E0648, - "`export_name` may not contain null characters" - ) - .emit(); - } - codegen_fn_attrs.export_name = Some(s); - } - } else if attr.has_name(sym::target_feature) { - if !tcx.is_closure(did.to_def_id()) - && tcx.fn_sig(did).unsafety() == hir::Unsafety::Normal - { - 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 - // ones. Other targets require that `#[target_feature]` is - // only applied to unsafe functions (pending the - // `target_feature_11` feature) because on most targets - // execution of instructions that are not supported is - // considered undefined behavior. For WebAssembly which is a - // 100% safe target at execution time it's not possible to - // execute undefined instructions, and even if a future - // feature was added in some form for this it would be a - // deterministic trap. There is no undefined behavior when - // executing WebAssembly so `#[target_feature]` is allowed - // on safe functions (but again, only for WebAssembly) - // - // Note that this is also allowed if `actually_rustdoc` so - // if a target is documenting some wasm-specific code then - // it's not spuriously denied. - } else if !tcx.features().target_feature_11 { - let mut err = feature_err( - &tcx.sess.parse_sess, - sym::target_feature_11, - attr.span, - "`#[target_feature(..)]` can only be applied to `unsafe` functions", - ); - err.span_label(tcx.def_span(did), "not an `unsafe` function"); - err.emit(); - } else { - check_target_feature_trait_unsafe(tcx, did, attr.span); - } - } - from_target_feature( - tcx, - attr, - supported_target_features, - &mut codegen_fn_attrs.target_features, - ); - } else if attr.has_name(sym::linkage) { - if let Some(val) = attr.value_str() { - let linkage = Some(linkage_by_name(tcx, did, val.as_str())); - if tcx.is_foreign_item(did) { - codegen_fn_attrs.import_linkage = linkage; - } else { - codegen_fn_attrs.linkage = linkage; - } - } - } else if attr.has_name(sym::link_section) { - if let Some(val) = attr.value_str() { - if val.as_str().bytes().any(|b| b == 0) { - let msg = format!( - "illegal null byte in link_section \ - value: `{}`", - &val - ); - tcx.sess.span_err(attr.span, &msg); - } else { - codegen_fn_attrs.link_section = Some(val); - } - } - } else if attr.has_name(sym::link_name) { - codegen_fn_attrs.link_name = attr.value_str(); - } else if attr.has_name(sym::link_ordinal) { - link_ordinal_span = Some(attr.span); - if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { - codegen_fn_attrs.link_ordinal = ordinal; - } - } else if attr.has_name(sym::no_sanitize) { - no_sanitize_span = Some(attr.span); - if let Some(list) = attr.meta_item_list() { - for item in list.iter() { - if item.has_name(sym::address) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS; - } else if item.has_name(sym::cfi) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI; - } else if item.has_name(sym::kcfi) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI; - } else if item.has_name(sym::memory) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; - } else if item.has_name(sym::memtag) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG; - } else if item.has_name(sym::shadow_call_stack) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK; - } else if item.has_name(sym::thread) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; - } else if item.has_name(sym::hwaddress) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS; - } else { - tcx.sess - .struct_span_err(item.span(), "invalid argument for `no_sanitize`") - .note("expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`") - .emit(); - } - } - } - } else if attr.has_name(sym::instruction_set) { - codegen_fn_attrs.instruction_set = match attr.meta_kind() { - Some(MetaItemKind::List(ref items)) => match items.as_slice() { - [NestedMetaItem::MetaItem(set)] => { - let segments = - set.path.segments.iter().map(|x| x.ident.name).collect::>(); - match segments.as_slice() { - [sym::arm, sym::a32] | [sym::arm, sym::t32] => { - if !tcx.sess.target.has_thumb_interworking { - struct_span_err!( - tcx.sess.diagnostic(), - attr.span, - E0779, - "target does not support `#[instruction_set]`" - ) - .emit(); - None - } else if segments[1] == sym::a32 { - Some(InstructionSetAttr::ArmA32) - } else if segments[1] == sym::t32 { - Some(InstructionSetAttr::ArmT32) - } else { - unreachable!() - } - } - _ => { - struct_span_err!( - tcx.sess.diagnostic(), - attr.span, - E0779, - "invalid instruction set specified", - ) - .emit(); - None - } - } - } - [] => { - struct_span_err!( - tcx.sess.diagnostic(), - attr.span, - E0778, - "`#[instruction_set]` requires an argument" - ) - .emit(); - None - } - _ => { - struct_span_err!( - tcx.sess.diagnostic(), - attr.span, - E0779, - "cannot specify more than one instruction set" - ) - .emit(); - None - } - }, - _ => { - struct_span_err!( - tcx.sess.diagnostic(), - attr.span, - E0778, - "must specify an instruction set" - ) - .emit(); - None - } - }; - } else if attr.has_name(sym::repr) { - codegen_fn_attrs.alignment = match attr.meta_item_list() { - Some(items) => match items.as_slice() { - [item] => match item.name_value_literal() { - Some((sym::align, literal)) => { - let alignment = rustc_attr::parse_alignment(&literal.kind); - - match alignment { - Ok(align) => Some(align), - Err(msg) => { - struct_span_err!( - tcx.sess.diagnostic(), - attr.span, - E0589, - "invalid `repr(align)` attribute: {}", - msg - ) - .emit(); - - None - } - } - } - _ => None, - }, - [] => None, - _ => None, - }, - None => None, - }; - } - } - - codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { - if !attr.has_name(sym::inline) { - return ia; - } - match attr.meta_kind() { - Some(MetaItemKind::Word) => InlineAttr::Hint, - Some(MetaItemKind::List(ref items)) => { - inline_span = Some(attr.span); - if items.len() != 1 { - struct_span_err!( - tcx.sess.diagnostic(), - attr.span, - E0534, - "expected one argument" - ) - .emit(); - InlineAttr::None - } else if list_contains_name(&items, sym::always) { - InlineAttr::Always - } else if list_contains_name(&items, sym::never) { - InlineAttr::Never - } else { - struct_span_err!( - tcx.sess.diagnostic(), - items[0].span(), - E0535, - "invalid argument" - ) - .help("valid inline arguments are `always` and `never`") - .emit(); - - InlineAttr::None - } - } - Some(MetaItemKind::NameValue(_)) => ia, - None => ia, - } - }); - - codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| { - if !attr.has_name(sym::optimize) { - return ia; - } - let err = |sp, s| struct_span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s).emit(); - match attr.meta_kind() { - Some(MetaItemKind::Word) => { - err(attr.span, "expected one argument"); - ia - } - Some(MetaItemKind::List(ref items)) => { - inline_span = Some(attr.span); - if items.len() != 1 { - err(attr.span, "expected one argument"); - OptimizeAttr::None - } else if list_contains_name(&items, sym::size) { - OptimizeAttr::Size - } else if list_contains_name(&items, sym::speed) { - OptimizeAttr::Speed - } else { - err(items[0].span(), "invalid argument"); - OptimizeAttr::None - } - } - Some(MetaItemKind::NameValue(_)) => ia, - None => ia, - } - }); - - // #73631: closures inherit `#[target_feature]` annotations - if tcx.features().target_feature_11 && tcx.is_closure(did.to_def_id()) { - let owner_id = tcx.parent(did.to_def_id()); - if tcx.def_kind(owner_id).has_codegen_attrs() { - codegen_fn_attrs - .target_features - .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied()); - } - } - - // If a function uses #[target_feature] it can't be inlined into general - // purpose functions as they wouldn't have the right target features - // enabled. For that reason we also forbid #[inline(always)] as it can't be - // respected. - if !codegen_fn_attrs.target_features.is_empty() { - if codegen_fn_attrs.inline == InlineAttr::Always { - if let Some(span) = inline_span { - tcx.sess.span_err( - span, - "cannot use `#[inline(always)]` with \ - `#[target_feature]`", - ); - } - } - } - - if !codegen_fn_attrs.no_sanitize.is_empty() { - if codegen_fn_attrs.inline == InlineAttr::Always { - if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) { - let hir_id = tcx.hir().local_def_id_to_hir_id(did); - tcx.struct_span_lint_hir( - lint::builtin::INLINE_NO_SANITIZE, - hir_id, - no_sanitize_span, - "`no_sanitize` will have no effect after inlining", - |lint| lint.span_note(inline_span, "inlining requested here"), - ) - } - } - } - - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE; - codegen_fn_attrs.inline = InlineAttr::Never; - } - - // Weak lang items have the same semantics as "std internal" symbols in the - // sense that they're preserved through all our LTO passes and only - // strippable by the linker. - // - // Additionally weak lang items have predetermined symbol names. - if WEAK_LANG_ITEMS.iter().any(|&l| tcx.lang_items().get(l) == Some(did.to_def_id())) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; - } - if let Some((name, _)) = lang_items::extract(attrs) - && let Some(lang_item) = LangItem::from_name(name) - && let Some(link_name) = lang_item.link_name() - { - codegen_fn_attrs.export_name = Some(link_name); - codegen_fn_attrs.link_name = Some(link_name); - } - check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span); - - // Internal symbols to the standard library all have no_mangle semantics in - // that they have defined symbol names present in the function name. This - // also applies to weak symbols where they all have known symbol names. - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; - } - - // Any linkage to LLVM intrinsics for now forcibly marks them all as never - // unwinds since LLVM sometimes can't handle codegen which `invoke`s - // intrinsic functions. - if let Some(name) = &codegen_fn_attrs.link_name { - if name.as_str().starts_with("llvm.") { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; - } - } - - codegen_fn_attrs -} - -/// Computes the set of target features used in a function for the purposes of -/// inline assembly. -fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet { - let mut target_features = tcx.sess.unstable_target_features.clone(); - if tcx.def_kind(did).has_codegen_attrs() { - let attrs = tcx.codegen_fn_attrs(did); - target_features.extend(&attrs.target_features); - match attrs.instruction_set { - None => {} - Some(InstructionSetAttr::ArmA32) => { - target_features.remove(&sym::thumb_mode); - } - Some(InstructionSetAttr::ArmT32) => { - target_features.insert(sym::thumb_mode); - } - } - } - - tcx.arena.alloc(target_features) -} - -/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller -/// applied to the method prototype. -fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - if let Some(impl_item) = tcx.opt_associated_item(def_id) - && let ty::AssocItemContainer::ImplContainer = impl_item.container - && let Some(trait_item) = impl_item.trait_item_def_id - { - return tcx - .codegen_fn_attrs(trait_item) - .flags - .intersects(CodegenFnAttrFlags::TRACK_CALLER); - } - - false -} - -fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { - use rustc_ast::{LitIntType, LitKind, MetaItemLit}; - if !tcx.features().raw_dylib && tcx.sess.target.arch == "x86" { - feature_err( - &tcx.sess.parse_sess, - sym::raw_dylib, - attr.span, - "`#[link_ordinal]` is unstable on x86", - ) - .emit(); - } - let meta_item_list = attr.meta_item_list(); - let meta_item_list = meta_item_list.as_deref(); - let sole_meta_list = match meta_item_list { - Some([item]) => item.lit(), - Some(_) => { - tcx.sess - .struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`") - .note("the attribute requires exactly one argument") - .emit(); - return None; - } - _ => None, - }; - if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = - sole_meta_list - { - // According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, - // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined - // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information - // to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t. - // - // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this: - // both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies - // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library - // for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import - // library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet - // if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment - // about LINK.EXE failing.) - if *ordinal <= u16::MAX as u128 { - Some(*ordinal as u16) - } else { - let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal); - tcx.sess - .struct_span_err(attr.span, &msg) - .note("the value may not exceed `u16::MAX`") - .emit(); - None - } - } else { - tcx.sess - .struct_span_err(attr.span, "illegal ordinal format in `link_ordinal`") - .note("an unsuffixed integer value, e.g., `1`, is expected") - .emit(); - None - } -} - -fn check_link_name_xor_ordinal( - tcx: TyCtxt<'_>, - codegen_fn_attrs: &CodegenFnAttrs, - inline_span: Option, -) { - if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() { - return; - } - let msg = "cannot use `#[link_name]` with `#[link_ordinal]`"; - if let Some(span) = inline_span { - tcx.sess.span_err(span, msg); - } else { - tcx.sess.err(msg); - } -} - -/// Checks the function annotated with `#[target_feature]` is not a safe -/// trait method implementation, reporting an error if it is. -fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { - let hir_id = tcx.hir().local_def_id_to_hir_id(id); - let node = tcx.hir().get(hir_id); - if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node { - let parent_id = tcx.hir().get_parent_item(hir_id); - let parent_item = tcx.hir().expect_item(parent_id.def_id); - if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = parent_item.kind { - tcx.sess - .struct_span_err( - attr_span, - "`#[target_feature(..)]` cannot be applied to safe trait method", - ) - .span_label(attr_span, "cannot be applied to safe trait method") - .span_label(tcx.def_span(id), "not an `unsafe` function") - .emit(); - } - } -} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index c92ab749bc1f7..d9697c63c56e1 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -253,13 +253,6 @@ pub struct ExternCrateNotIdiomatic { pub suggestion_code: String, } -#[derive(Diagnostic)] -#[diag(hir_analysis_expected_used_symbol)] -pub struct ExpectedUsedSymbol { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(hir_analysis_const_impl_for_non_const_trait)] pub struct ConstImplForNonConstTrait { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index b15c086ffad57..a7eae392de1dd 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -209,7 +209,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ProbeScope::TraitsInScope, ) { Ok(ref new_pick) if pick.differs_from(new_pick) => { - needs_mut = true; + needs_mut = new_pick.self_ty.ref_mutability() != self_ty.ref_mutability(); } _ => {} } diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 74c4c65cc1729..c4f11472d5548 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -517,6 +517,6 @@ pub struct MismatchedStaticLifetime<'a> { pub expl: Option>, #[subdiagnostic] pub does_not_outlive_static_from_impl: DoesNotOutliveStaticFromImpl, - #[subdiagnostic(eager)] + #[subdiagnostic] pub implicit_static_lifetimes: Vec, } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 9f2ac5112f1cd..82f6812026a7d 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -372,46 +372,21 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { } } (Meta::Path(_), "subdiagnostic") => { - return Ok(quote! { #diag.subdiagnostic(#binding); }); + if FieldInnerTy::from_type(&info.binding.ast().ty).will_iterate() { + let DiagnosticDeriveKind::Diagnostic { handler } = &self.parent.kind else { + // No eager translation for lints. + return Ok(quote! { #diag.subdiagnostic(#binding); }); + }; + return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); }); + } else { + return Ok(quote! { #diag.subdiagnostic(#binding); }); + } } - (Meta::NameValue(_), "subdiagnostic") => { + (Meta::List(_), "subdiagnostic") => { throw_invalid_attr!(attr, &meta, |diag| { - diag.help("`eager` is the only supported nested attribute for `subdiagnostic`") + diag.help("`subdiagnostic` does not support nested attributes") }) } - (Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => { - if nested.len() != 1 { - throw_invalid_attr!(attr, &meta, |diag| { - diag.help( - "`eager` is the only supported nested attribute for `subdiagnostic`", - ) - }) - } - - let handler = match &self.parent.kind { - DiagnosticDeriveKind::Diagnostic { handler } => handler, - DiagnosticDeriveKind::LintDiagnostic => { - throw_invalid_attr!(attr, &meta, |diag| { - diag.help("eager subdiagnostics are not supported on lints") - }) - } - }; - - let nested_attr = nested.first().expect("pop failed for single element list"); - match nested_attr { - NestedMeta::Meta(meta @ Meta::Path(_)) - if meta.path().segments.last().unwrap().ident.to_string().as_str() - == "eager" => - { - return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); }); - } - _ => { - throw_invalid_nested_attr!(attr, nested_attr, |diag| { - diag.help("`eager` is the only supported nested attribute for `subdiagnostic`") - }) - } - } - } _ => (), } diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index 7a20eaceba024..cf2f04c7486b8 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -49,7 +49,7 @@ pub struct Cycle { #[primary_span] pub span: Span, pub stack_bottom: String, - #[subdiagnostic(eager)] + #[subdiagnostic] pub cycle_stack: Vec, #[subdiagnostic] pub stack_count: StackCount, diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index ee492f802a70c..ee1e7ae6b9c5f 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -176,7 +176,7 @@ impl ExprParenthesesNeeded { #[derive(Diagnostic)] #[diag(session_skipping_const_checks)] pub struct SkippingConstChecks { - #[subdiagnostic(eager)] + #[subdiagnostic] pub unleashed_features: Vec, } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs index 752b53fbc3f9a..0c1717cff332c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs @@ -1,52 +1,101 @@ use rustc_hir::def_id::DefId; -use rustc_infer::infer::InferCtxt; +use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime}; +use rustc_infer::traits::util::elaborate_predicates_with_span; use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation}; -use rustc_span::DUMMY_SP; +use rustc_middle::ty; +use rustc_span::{Span, DUMMY_SP}; use crate::traits::ObligationCtxt; +pub enum Ambiguity { + DefId(DefId), + ParamEnv(Span), +} + pub fn recompute_applicable_impls<'tcx>( infcx: &InferCtxt<'tcx>, obligation: &TraitObligation<'tcx>, -) -> Vec { +) -> Vec { let tcx = infcx.tcx; let param_env = obligation.param_env; - let dummy_cause = ObligationCause::dummy(); + let impl_may_apply = |impl_def_id| { let ocx = ObligationCtxt::new_in_snapshot(infcx); let placeholder_obligation = infcx.replace_bound_vars_with_placeholders(obligation.predicate); let obligation_trait_ref = - ocx.normalize(&dummy_cause, param_env, placeholder_obligation.trait_ref); + ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref); let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs); let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref); - if let Err(_) = ocx.eq(&dummy_cause, param_env, obligation_trait_ref, impl_trait_ref) { + if let Err(_) = + ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref) + { return false; } let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs); - ocx.register_obligations( - impl_predicates - .predicates - .iter() - .map(|&predicate| Obligation::new(tcx, dummy_cause.clone(), param_env, predicate)), + ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| { + Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate) + })); + + ocx.select_where_possible().is_empty() + }; + + let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| { + let ocx = ObligationCtxt::new_in_snapshot(infcx); + let placeholder_obligation = + infcx.replace_bound_vars_with_placeholders(obligation.predicate); + let obligation_trait_ref = + ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref); + + let param_env_predicate = infcx.replace_bound_vars_with_fresh_vars( + DUMMY_SP, + LateBoundRegionConversionTime::HigherRankedType, + poly_trait_predicate, ); + let param_env_trait_ref = + ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref); + + if let Err(_) = + ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, param_env_trait_ref) + { + return false; + } ocx.select_where_possible().is_empty() }; - let mut impls = Vec::new(); + let mut ambiguities = Vec::new(); + tcx.for_each_relevant_impl( obligation.predicate.def_id(), obligation.predicate.skip_binder().trait_ref.self_ty(), |impl_def_id| { - if infcx.probe(move |_snapshot| impl_may_apply(impl_def_id)) { - impls.push(impl_def_id) + if infcx.probe(|_| impl_may_apply(impl_def_id)) { + ambiguities.push(Ambiguity::DefId(impl_def_id)) } }, ); - impls + + let predicates = + tcx.predicates_of(obligation.cause.body_id.owner.to_def_id()).instantiate_identity(tcx); + for obligation in + elaborate_predicates_with_span(tcx, std::iter::zip(predicates.predicates, predicates.spans)) + { + let kind = obligation.predicate.kind(); + if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder() + && param_env_candidate_may_apply(kind.rebind(trait_pred)) + { + if kind.rebind(trait_pred.trait_ref) == ty::TraitRef::identity(tcx, trait_pred.def_id()) { + ambiguities.push(Ambiguity::ParamEnv(tcx.def_span(trait_pred.def_id()))) + } else { + ambiguities.push(Ambiguity::ParamEnv(obligation.cause.span)) + } + } + } + + ambiguities } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 30ff07ee6c372..9e2a0a927e1bb 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1482,7 +1482,7 @@ trait InferCtxtPrivExt<'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[DefId], + impls: &[ambiguity::Ambiguity], predicate: ty::Predicate<'tcx>, ); @@ -2174,13 +2174,22 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut selcx = SelectionContext::new(&self); match selcx.select_from_obligation(&obligation) { Ok(None) => { - let impls = ambiguity::recompute_applicable_impls(self.infcx, &obligation); + let ambiguities = + ambiguity::recompute_applicable_impls(self.infcx, &obligation); let has_non_region_infer = trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_infer()); // It doesn't make sense to talk about applicable impls if there are more // than a handful of them. - if impls.len() > 1 && impls.len() < 5 && has_non_region_infer { - self.annotate_source_of_ambiguity(&mut err, &impls, predicate); + if ambiguities.len() > 1 && ambiguities.len() < 5 && has_non_region_infer { + if self.tainted_by_errors().is_some() && subst.is_none() { + // If `subst.is_none()`, then this is probably two param-env + // candidates or impl candidates that are equal modulo lifetimes. + // Therefore, if we've already emitted an error, just skip this + // one, since it's not particularly actionable. + err.cancel(); + return; + } + self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate); } else { if self.tainted_by_errors().is_some() { err.cancel(); @@ -2466,21 +2475,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[DefId], + ambiguities: &[ambiguity::Ambiguity], predicate: ty::Predicate<'tcx>, ) { let mut spans = vec![]; let mut crates = vec![]; let mut post = vec![]; - for def_id in impls { - match self.tcx.span_of_impl(*def_id) { - Ok(span) => spans.push(span), - Err(name) => { - crates.push(name); - if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { - post.push(header); + let mut has_param_env = false; + for ambiguity in ambiguities { + match ambiguity { + ambiguity::Ambiguity::DefId(impl_def_id) => { + match self.tcx.span_of_impl(*impl_def_id) { + Ok(span) => spans.push(span), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) { + post.push(header); + } + } } } + ambiguity::Ambiguity::ParamEnv(span) => { + has_param_env = true; + spans.push(*span); + } } } let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); @@ -2504,7 +2522,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } - let msg = format!("multiple `impl`s satisfying `{}` found", predicate); + let msg = format!( + "multiple `impl`s{} satisfying `{}` found", + if has_param_env { " or `where` clauses" } else { "" }, + predicate + ); let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { format!(":\n{}", post.iter().map(|p| format!("- {}", p)).collect::>().join("\n"),) } else if post.len() == 1 { diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index d1ba46ad30de6..5a0397a3d123c 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -467,7 +467,7 @@ jobs: - name: dist-x86_64-apple env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin - RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false --set rust.lto=thin + RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.7 SELECT_XCODE: /Applications/Xcode_13.4.1.app diff --git a/src/test/incremental/hashes/loop_expressions.rs b/src/test/incremental/hashes/loop_expressions.rs index a12cd0d021e59..ada541e644a31 100644 --- a/src/test/incremental/hashes/loop_expressions.rs +++ b/src/test/incremental/hashes/loop_expressions.rs @@ -187,9 +187,9 @@ pub fn change_continue_label() { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, typeck")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, typeck, optimized_mir")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, optimized_mir")] #[rustc_clean(cfg="cfail6")] pub fn change_continue_label() { let mut _x = 0; diff --git a/src/test/incremental/hashes/while_let_loops.rs b/src/test/incremental/hashes/while_let_loops.rs index f81855e42becb..88fd4d89b2827 100644 --- a/src/test/incremental/hashes/while_let_loops.rs +++ b/src/test/incremental/hashes/while_let_loops.rs @@ -158,9 +158,9 @@ pub fn change_break_label() { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes,typeck")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail6")] pub fn change_break_label() { let mut _x = 0; @@ -210,9 +210,9 @@ pub fn change_continue_label() { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes,typeck")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail6")] pub fn change_continue_label() { let mut _x = 0; diff --git a/src/test/incremental/hashes/while_loops.rs b/src/test/incremental/hashes/while_loops.rs index e432cf8fe4cc3..9b4d23757b871 100644 --- a/src/test/incremental/hashes/while_loops.rs +++ b/src/test/incremental/hashes/while_loops.rs @@ -158,9 +158,9 @@ pub fn change_break_label() { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,typeck")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes,optimized_mir,typeck")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes,optimized_mir")] #[rustc_clean(cfg="cfail6")] pub fn change_break_label() { let mut _x = 0; @@ -212,9 +212,9 @@ pub fn change_continue_label() { } #[cfg(not(any(cfail1,cfail4)))] -#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes,typeck,optimized_mir")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes,optimized_mir")] #[rustc_clean(cfg="cfail6")] pub fn change_continue_label() { let mut _x = 0; diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index cb4cd466590b6..c19b639a8d581 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -683,7 +683,7 @@ struct RawIdentDiagnosticArg { #[diag(compiletest_example)] struct SubdiagnosticBad { #[subdiagnostic(bad)] - //~^ ERROR `#[subdiagnostic(bad)]` is not a valid attribute + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute note: Note, } @@ -707,7 +707,7 @@ struct SubdiagnosticBadTwice { #[diag(compiletest_example)] struct SubdiagnosticBadLitStr { #[subdiagnostic("bad")] - //~^ ERROR `#[subdiagnostic("...")]` is not a valid attribute + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute note: Note, } @@ -723,6 +723,7 @@ struct SubdiagnosticEagerLint { #[diag(compiletest_example)] struct SubdiagnosticEagerCorrect { #[subdiagnostic(eager)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute note: Note, } @@ -743,6 +744,7 @@ pub(crate) struct SubdiagnosticWithSuggestion { #[diag(compiletest_example)] struct SubdiagnosticEagerSuggestion { #[subdiagnostic(eager)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute sub: SubdiagnosticWithSuggestion, } diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index b4c211db47cd9..d0be7be66b868 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -533,21 +533,19 @@ LL | #[label] | = help: `#[label]` and `#[suggestion]` can only be applied to fields -error: `#[subdiagnostic(bad)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:685:21 +error: `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:685:5 | LL | #[subdiagnostic(bad)] - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^ | - = help: `eager` is the only supported nested attribute for `subdiagnostic` + = help: `subdiagnostic` does not support nested attributes error: `#[subdiagnostic = ...]` is not a valid attribute --> $DIR/diagnostic-derive.rs:693:5 | LL | #[subdiagnostic = "bad"] | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: `eager` is the only supported nested attribute for `subdiagnostic` error: `#[subdiagnostic(...)]` is not a valid attribute --> $DIR/diagnostic-derive.rs:701:5 @@ -555,15 +553,15 @@ error: `#[subdiagnostic(...)]` is not a valid attribute LL | #[subdiagnostic(bad, bad)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: `eager` is the only supported nested attribute for `subdiagnostic` + = help: `subdiagnostic` does not support nested attributes -error: `#[subdiagnostic("...")]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:709:21 +error: `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:709:5 | LL | #[subdiagnostic("bad")] - | ^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: `eager` is the only supported nested attribute for `subdiagnostic` + = help: `subdiagnostic` does not support nested attributes error: `#[subdiagnostic(...)]` is not a valid attribute --> $DIR/diagnostic-derive.rs:717:5 @@ -571,22 +569,38 @@ error: `#[subdiagnostic(...)]` is not a valid attribute LL | #[subdiagnostic(eager)] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: eager subdiagnostics are not supported on lints + = help: `subdiagnostic` does not support nested attributes + +error: `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:725:5 + | +LL | #[subdiagnostic(eager)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `subdiagnostic` does not support nested attributes + +error: `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:746:5 + | +LL | #[subdiagnostic(eager)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `subdiagnostic` does not support nested attributes error: expected at least one string literal for `code(...)` - --> $DIR/diagnostic-derive.rs:775:18 + --> $DIR/diagnostic-derive.rs:777:18 | LL | #[suggestion(code())] | ^^^^^^ error: `code(...)` must contain only string literals - --> $DIR/diagnostic-derive.rs:783:23 + --> $DIR/diagnostic-derive.rs:785:23 | LL | #[suggestion(code(foo))] | ^^^ error: `code = "..."`/`code(...)` must contain only string literals - --> $DIR/diagnostic-derive.rs:791:18 + --> $DIR/diagnostic-derive.rs:793:18 | LL | #[suggestion(code = 3)] | ^^^^^^^^ @@ -665,7 +679,7 @@ LL | arg: impl IntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg` = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 83 previous errors +error: aborting due to 85 previous errors Some errors have detailed explanations: E0277, E0425. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/track-caller/async-block.rs b/src/test/ui/async-await/track-caller/async-block.rs new file mode 100644 index 0000000000000..8e81387c34bd0 --- /dev/null +++ b/src/test/ui/async-await/track-caller/async-block.rs @@ -0,0 +1,9 @@ +// edition:2021 + +#![feature(closure_track_caller, stmt_expr_attributes)] + +fn main() { + let _ = #[track_caller] async { + //~^ ERROR attribute should be applied to a function definition [E0739] + }; +} diff --git a/src/test/ui/async-await/track-caller/async-block.stderr b/src/test/ui/async-await/track-caller/async-block.stderr new file mode 100644 index 0000000000000..407439921c0d4 --- /dev/null +++ b/src/test/ui/async-await/track-caller/async-block.stderr @@ -0,0 +1,12 @@ +error[E0739]: attribute should be applied to a function definition + --> $DIR/async-block.rs:6:13 + | +LL | let _ = #[track_caller] async { + | _____________^^^^^^^^^^^^^^^_- +LL | | +LL | | }; + | |_____- not a function definition + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0739`. diff --git a/src/test/ui/async-await/track-caller/async-closure-gate.rs b/src/test/ui/async-await/track-caller/async-closure-gate.rs new file mode 100644 index 0000000000000..9593fdb1908e2 --- /dev/null +++ b/src/test/ui/async-await/track-caller/async-closure-gate.rs @@ -0,0 +1,10 @@ +// edition:2021 + +#![feature(async_closure, stmt_expr_attributes)] + +fn main() { + let _ = #[track_caller] async || { + //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + //~| ERROR `#[track_caller]` on closures is currently unstable [E0658] + }; +} diff --git a/src/test/ui/async-await/track-caller/async-closure-gate.stderr b/src/test/ui/async-await/track-caller/async-closure-gate.stderr new file mode 100644 index 0000000000000..be3d110eccdb0 --- /dev/null +++ b/src/test/ui/async-await/track-caller/async-closure-gate.stderr @@ -0,0 +1,25 @@ +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:6:13 + | +LL | let _ = #[track_caller] async || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:6:38 + | +LL | let _ = #[track_caller] async || { + | ______________________________________^ +LL | | +LL | | +LL | | }; + | |_____^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/async-await/track-caller/panic-track-caller.rs b/src/test/ui/async-await/track-caller/panic-track-caller.rs index 5ebfeb3f36ac1..066cf97628fa5 100644 --- a/src/test/ui/async-await/track-caller/panic-track-caller.rs +++ b/src/test/ui/async-await/track-caller/panic-track-caller.rs @@ -1,7 +1,7 @@ // run-pass // edition:2021 // needs-unwind -#![feature(closure_track_caller)] +#![feature(closure_track_caller, async_closure, stmt_expr_attributes)] use std::future::Future; use std::panic; @@ -67,6 +67,13 @@ async fn foo_assoc() { Foo::bar_assoc().await } +async fn foo_closure() { + let c = #[track_caller] async || { + panic!(); + }; + c().await +} + fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { let loc = Arc::new(Mutex::new(None)); @@ -87,4 +94,5 @@ fn main() { assert_eq!(panicked_at(|| block_on(foo())), 41); assert_eq!(panicked_at(|| block_on(foo_track_caller())), 54); assert_eq!(panicked_at(|| block_on(foo_assoc())), 67); + assert_eq!(panicked_at(|| block_on(foo_closure())), 74); } diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-72787.min.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-72787.min.stderr index 4d0d0253f1b6d..0af5493f81675 100644 --- a/src/test/ui/const-generics/generic_const_exprs/issue-72787.min.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/issue-72787.min.stderr @@ -40,7 +40,17 @@ error[E0283]: type annotations needed: cannot satisfy `IsLessOrEqual: True LL | IsLessOrEqual: True, | ^^^^ | - = note: cannot satisfy `IsLessOrEqual: True` +note: multiple `impl`s or `where` clauses satisfying `IsLessOrEqual: True` found + --> $DIR/issue-72787.rs:10:1 + | +LL | impl True for IsLessOrEqual where + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | IsLessOrEqual: True, + | ^^^^ +... +LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, + | ^^^^ error[E0283]: type annotations needed: cannot satisfy `IsLessOrEqual: True` --> $DIR/issue-72787.rs:21:26 @@ -48,7 +58,17 @@ error[E0283]: type annotations needed: cannot satisfy `IsLessOrEqual: True LL | IsLessOrEqual: True, | ^^^^ | - = note: cannot satisfy `IsLessOrEqual: True` +note: multiple `impl`s or `where` clauses satisfying `IsLessOrEqual: True` found + --> $DIR/issue-72787.rs:10:1 + | +LL | impl True for IsLessOrEqual where + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | IsLessOrEqual: True, + | ^^^^ +... +LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, + | ^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch.rs b/src/test/ui/illegal-sized-bound/mutability-mismatch.rs new file mode 100644 index 0000000000000..deb84f6fe97cf --- /dev/null +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch.rs @@ -0,0 +1,34 @@ +struct MutType; + +pub trait MutTrait { + fn function(&mut self) + where + Self: Sized; + //~^ this has a `Sized` requirement +} + +impl MutTrait for MutType { + fn function(&mut self) {} +} + +struct Type; + +pub trait Trait { + fn function(&self) + where + Self: Sized; + //~^ this has a `Sized` requirement +} + +impl Trait for Type { + fn function(&self) {} +} + +fn main() { + (&MutType as &dyn MutTrait).function(); + //~^ ERROR the `function` method cannot be invoked on a trait object + //~| NOTE you need `&mut dyn MutTrait` instead of `&dyn MutTrait` + (&mut Type as &mut dyn Trait).function(); + //~^ ERROR the `function` method cannot be invoked on a trait object + //~| NOTE you need `&dyn Trait` instead of `&mut dyn Trait` +} diff --git a/src/test/ui/illegal-sized-bound/mutability-mismatch.stderr b/src/test/ui/illegal-sized-bound/mutability-mismatch.stderr new file mode 100644 index 0000000000000..dbbf79a4f1a03 --- /dev/null +++ b/src/test/ui/illegal-sized-bound/mutability-mismatch.stderr @@ -0,0 +1,24 @@ +error: the `function` method cannot be invoked on a trait object + --> $DIR/mutability-mismatch.rs:28:33 + | +LL | Self: Sized; + | ----- this has a `Sized` requirement +... +LL | (&MutType as &dyn MutTrait).function(); + | ^^^^^^^^ + | + = note: you need `&mut dyn MutTrait` instead of `&dyn MutTrait` + +error: the `function` method cannot be invoked on a trait object + --> $DIR/mutability-mismatch.rs:31:35 + | +LL | Self: Sized; + | ----- this has a `Sized` requirement +... +LL | (&mut Type as &mut dyn Trait).function(); + | ^^^^^^^^ + | + = note: you need `&dyn Trait` instead of `&mut dyn Trait` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/illegal-sized-bound/regular.rs b/src/test/ui/illegal-sized-bound/regular.rs new file mode 100644 index 0000000000000..7abd27ef98318 --- /dev/null +++ b/src/test/ui/illegal-sized-bound/regular.rs @@ -0,0 +1,32 @@ +struct MutType; + +pub trait MutTrait { + fn function(&mut self) + where + Self: Sized; + //~^ this has a `Sized` requirement +} + +impl MutTrait for MutType { + fn function(&mut self) {} +} + +struct Type; + +pub trait Trait { + fn function(&self) + where + Self: Sized; + //~^ this has a `Sized` requirement +} + +impl Trait for Type { + fn function(&self) {} +} + +fn main() { + (&mut MutType as &mut dyn MutTrait).function(); + //~^ ERROR the `function` method cannot be invoked on a trait object + (&Type as &dyn Trait).function(); + //~^ ERROR the `function` method cannot be invoked on a trait object +} diff --git a/src/test/ui/illegal-sized-bound/regular.stderr b/src/test/ui/illegal-sized-bound/regular.stderr new file mode 100644 index 0000000000000..7f3744145d927 --- /dev/null +++ b/src/test/ui/illegal-sized-bound/regular.stderr @@ -0,0 +1,20 @@ +error: the `function` method cannot be invoked on a trait object + --> $DIR/regular.rs:28:41 + | +LL | Self: Sized; + | ----- this has a `Sized` requirement +... +LL | (&mut MutType as &mut dyn MutTrait).function(); + | ^^^^^^^^ + +error: the `function` method cannot be invoked on a trait object + --> $DIR/regular.rs:30:27 + | +LL | Self: Sized; + | ----- this has a `Sized` requirement +... +LL | (&Type as &dyn Trait).function(); + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/issues/issue-21974.stderr b/src/test/ui/issues/issue-21974.stderr index 4e010a13653e7..2d60b18b1f208 100644 --- a/src/test/ui/issues/issue-21974.stderr +++ b/src/test/ui/issues/issue-21974.stderr @@ -4,7 +4,13 @@ error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo` LL | where &'a T : Foo, | ^^^ | - = note: cannot satisfy `&'a T: Foo` +note: multiple `impl`s or `where` clauses satisfying `&'a T: Foo` found + --> $DIR/issue-21974.rs:11:19 + | +LL | where &'a T : Foo, + | ^^^ +LL | &'b T : Foo + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-24424.stderr b/src/test/ui/issues/issue-24424.stderr index 8f3b2ac73199c..50d7f988e194c 100644 --- a/src/test/ui/issues/issue-24424.stderr +++ b/src/test/ui/issues/issue-24424.stderr @@ -4,7 +4,11 @@ error[E0283]: type annotations needed: cannot satisfy `T0: Trait0<'l0>` LL | impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {} | ^^^^^^^^^^^ | - = note: cannot satisfy `T0: Trait0<'l0>` +note: multiple `impl`s or `where` clauses satisfying `T0: Trait0<'l0>` found + --> $DIR/issue-24424.rs:4:57 + | +LL | impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {} + | ^^^^^^^^^^^ ^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/lifetimes/conflicting-bounds.rs b/src/test/ui/lifetimes/conflicting-bounds.rs new file mode 100644 index 0000000000000..f37f163dbb639 --- /dev/null +++ b/src/test/ui/lifetimes/conflicting-bounds.rs @@ -0,0 +1,11 @@ +//~ type annotations needed: cannot satisfy `Self: Gen<'source>` + +pub trait Gen<'source> { + type Output; + + fn gen(&self) -> T + where + Self: for<'s> Gen<'s, Output = T>; +} + +fn main() {} diff --git a/src/test/ui/lifetimes/conflicting-bounds.stderr b/src/test/ui/lifetimes/conflicting-bounds.stderr new file mode 100644 index 0000000000000..42aa393667dc2 --- /dev/null +++ b/src/test/ui/lifetimes/conflicting-bounds.stderr @@ -0,0 +1,14 @@ +error[E0283]: type annotations needed: cannot satisfy `Self: Gen<'source>` + | +note: multiple `impl`s or `where` clauses satisfying `Self: Gen<'source>` found + --> $DIR/conflicting-bounds.rs:3:1 + | +LL | pub trait Gen<'source> { + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | Self: for<'s> Gen<'s, Output = T>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/src/test/ui/lifetimes/issue-34979.stderr b/src/test/ui/lifetimes/issue-34979.stderr index 5832c4d173c10..3d4208031cd06 100644 --- a/src/test/ui/lifetimes/issue-34979.stderr +++ b/src/test/ui/lifetimes/issue-34979.stderr @@ -4,7 +4,16 @@ error[E0283]: type annotations needed: cannot satisfy `&'a (): Foo` LL | &'a (): Foo, | ^^^ | - = note: cannot satisfy `&'a (): Foo` +note: multiple `impl`s or `where` clauses satisfying `&'a (): Foo` found + --> $DIR/issue-34979.rs:2:1 + | +LL | impl<'a, T> Foo for &'a T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | &'a (): Foo, + | ^^^ +LL | &'static (): Foo; + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/traits/issue-85735.stderr b/src/test/ui/traits/issue-85735.stderr index fa280135beb2d..9e80497ca6e92 100644 --- a/src/test/ui/traits/issue-85735.stderr +++ b/src/test/ui/traits/issue-85735.stderr @@ -4,7 +4,14 @@ error[E0283]: type annotations needed: cannot satisfy `T: FnMut<(&'a (),)>` LL | T: FnMut(&'a ()), | ^^^^^^^^^^^^^ | - = note: cannot satisfy `T: FnMut<(&'a (),)>` +note: multiple `impl`s or `where` clauses satisfying `T: FnMut<(&'a (),)>` found + --> $DIR/issue-85735.rs:7:8 + | +LL | T: FnMut(&'a ()), + | ^^^^^^^^^^^^^ +LL | +LL | T: FnMut(&'b ()), + | ^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type/type-check/issue-40294.stderr b/src/test/ui/type/type-check/issue-40294.stderr index 75feb5698eb63..d15fd23418bb0 100644 --- a/src/test/ui/type/type-check/issue-40294.stderr +++ b/src/test/ui/type/type-check/issue-40294.stderr @@ -4,7 +4,13 @@ error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo` LL | where &'a T : Foo, | ^^^ | - = note: cannot satisfy `&'a T: Foo` +note: multiple `impl`s or `where` clauses satisfying `&'a T: Foo` found + --> $DIR/issue-40294.rs:6:19 + | +LL | where &'a T : Foo, + | ^^^ +LL | &'b T : Foo + | ^^^ error: aborting due to previous error