diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 1827901fc041e..390a41c64931b 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -19,8 +19,13 @@ "src/doc/book", "src/doc/reference" ], - // Require manual approval from the Dependency Dashboard before opening PRs + // Require manual approval from the Dependency Dashboard before opening PRs, + // except for the update types explicitly configured below. "dependencyDashboardApproval": true, + // No dashboard approval necessary for security updates + "vulnerabilityAlerts": { + "dependencyDashboardApproval": false + }, // Renovate shouldn't update a PR if it is in the bors merge queue. "stopUpdatingLabel": "S-waiting-on-bors", "packageRules": [ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f918e3883d6a..0b0c190a533ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,7 @@ on: branches: - automation/bors/auto - automation/bors/try + - automation/bors/try-perf - try-perf pull_request: branches: @@ -34,7 +35,7 @@ concurrency: # We add an exception for try builds (automation/bors/try branch) and unrolled rollup builds # (try-perf), which are all triggered on the same branch, but which should be able to run # concurrently. - group: ${{ github.workflow }}-${{ ((github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try') && github.sha) || github.ref }} + group: ${{ github.workflow }}-${{ ((github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try-perf' || github.ref == 'refs/heads/automation/bors/try') && github.sha) || github.ref }} cancel-in-progress: true env: TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate" @@ -79,7 +80,7 @@ jobs: # access the environment. # # We only enable the environment for the rust-lang/rust repository, so that CI works on forks. - environment: ${{ ((github.repository == 'rust-lang/rust' && (github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try' || github.ref == 'refs/heads/automation/bors/auto')) && 'bors') || '' }} + environment: ${{ ((github.repository == 'rust-lang/rust' && (github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try' || github.ref == 'refs/heads/automation/bors/try-perf' || github.ref == 'refs/heads/automation/bors/auto')) && 'bors') || '' }} env: CI_JOB_NAME: ${{ matrix.name }} CI_JOB_DOC_URL: ${{ matrix.doc_url }} diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index c6124fdfff38c..fd3a00d56fe0b 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -93,7 +93,7 @@ impl<'hir> LoweringContext<'_, 'hir> { match asm::InlineAsmClobberAbi::parse( asm_arch, &self.tcx.sess.target, - &self.tcx.sess.unstable_target_features, + &self.tcx.sess.internal_target_features, *abi_name, ) { Ok(abi) => { diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index 4d9bc09faeecb..911ec5956006d 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -662,10 +662,10 @@ impl<'hir> LoweringContext<'_, 'hir> { p.def_id.to_def_id(), ); - self.create_resolved_path(res, p.name.ident(), p.span) + self.create_resolved_qpath(res, p.name.ident(), p.span) } - pub(super) fn create_resolved_path( + pub(super) fn create_resolved_qpath( &mut self, res: Res, ident: Ident, diff --git a/compiler/rustc_ast_lowering/src/delegation/mod.rs b/compiler/rustc_ast_lowering/src/delegation/mod.rs index d0033ba0e472e..02fd6de314d3a 100644 --- a/compiler/rustc_ast_lowering/src/delegation/mod.rs +++ b/compiler/rustc_ast_lowering/src/delegation/mod.rs @@ -439,7 +439,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let ident = Ident::new(kw::SelfUpper, span); - let path = self.create_resolved_path(res, ident, span); + let path = self.create_resolved_qpath(res, ident, span); // FIXME(fn_delegation): add default `..` for all other fields. let initializer = hir::ExprKind::Struct( @@ -454,7 +454,14 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::StructTailExpr::None, ); - self.arena.alloc(self.mk_expr(initializer, span)) + let expr = self.mk_expr(initializer, span); + + let path = self.make_lang_item_qpath(hir::LangItem::FromFn, span, None); + let path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span)); + + let call = hir::ExprKind::Call(path, self.arena.alloc_slice(&[expr])); + + self.arena.alloc(self.mk_expr(call, span)) } else { self.arena.alloc(call) }; diff --git a/compiler/rustc_ast_lowering/src/delegation/resolution.rs b/compiler/rustc_ast_lowering/src/delegation/resolution.rs index 1d9bcef7ac5a7..dd1b9518e6d7f 100644 --- a/compiler/rustc_ast_lowering/src/delegation/resolution.rs +++ b/compiler/rustc_ast_lowering/src/delegation/resolution.rs @@ -5,10 +5,10 @@ use hir::def::DefKind; use rustc_ast::{self as ast, Delegation, DelegationSource, NodeId}; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir as hir; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_middle::{span_bug, ty}; use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::{ErrorGuaranteed, Span, kw}; +use rustc_span::{ErrorGuaranteed, Span}; use crate::delegation::generics::GenericsGenerationResults; use crate::delegation::resolution::resolver::DelegationResolver; @@ -31,7 +31,7 @@ pub(super) struct ParamInfo { pub splatted: Option, } -#[derive(Default)] +#[derive(Default, Debug)] pub(super) struct SigMapping { pub map_return: bool, pub arguments_to_map: FxIndexSet, @@ -254,17 +254,52 @@ impl<'tcx> DelegationResolver<'_, 'tcx> { } if self.can_perform_self_mapping(delegation, parent)? { - // FIXME(fn_delegation): support heuristics for mapping of complex - // return types: `Self` -> `Box>>` - mapping.map_return = sig.output().is_param(0); + /// Finds `Self` generic param only in ADT or references, so we avoid cases like + /// `Self::Item` which will return true if `output.contains(...)` will be used. + struct SelfFinder; + + impl<'tcx> TypeVisitor> for SelfFinder { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + match t.kind() { + ty::Adt(_, args) => { + if args + .iter() + .flat_map(|arg| arg.as_type()) + .any(|type_arg| type_arg.is_self_param()) + { + return ControlFlow::Break(()); + } + + t.super_visit_with(self) + } + ty::Ref(_, ref_t, _) => { + if ref_t.is_self_param() { + return ControlFlow::Break(()); + } + + t.super_visit_with(self) + } + _ => ControlFlow::Continue(()), + } + } + } + + impl SelfFinder { + fn contains_self(t: Ty<'_>) -> bool { + t.is_self_param() || t.visit_with(&mut SelfFinder).is_break() + } + } + + mapping.map_return = SelfFinder::contains_self(sig.output()); - let self_param = Ty::new_param(self.tcx(), 0, kw::SelfUpper); let arguments_to_map = sig .inputs() .iter() .enumerate() .skip(1) // Already checked above. - .filter_map(|(idx, param)| param.contains(self_param).then_some(idx)); + .filter_map(|(idx, ¶m)| SelfFinder::contains_self(param).then_some(idx)); mapping.arguments_to_map.extend(arguments_to_map); } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 1264ad6597561..a813e0ba02a89 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -1,16 +1,20 @@ use std::ops::Range; +use rustc_ast::PathSegment; +use rustc_errors::{Diagnostic, MultiSpan}; use rustc_hir::attrs::diagnostic::{ Directive, Filter, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, Name, NameValue, Piece, Predicate, }; +use rustc_lint_defs::LintId; use rustc_parse_format::{ Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position, }; use rustc_session::lint::builtin::{ MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FILTERS, - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, }; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{Ident, InnerSpan, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -20,6 +24,7 @@ use crate::diagnostics::{ MissingOptionsForDiagnosticAttribute, NonMetaItemDiagnosticAttribute, WrappedParserError, }; use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser}; +use crate::{EmitAttribute, diagnostics}; pub(crate) mod do_not_recommend; pub(crate) mod on_const; @@ -30,6 +35,67 @@ pub(crate) mod on_unknown; pub(crate) mod on_unmatched_args; pub(crate) mod opaque; +impl<'sess> crate::AttributeParser<'sess> { + pub(crate) fn unknown_diagnostic_attr( + &self, + segment: &PathSegment, + mut emit_lint: impl FnMut(LintId, MultiSpan, EmitAttribute), + ) { + const DIAGNOSTIC_ATTRIBUTES: [( + Symbol, /* name */ + Option, /* feature gate */ + ); 8] = [ + (sym::on_unimplemented, None), + (sym::do_not_recommend, None), + (sym::on_move, Some(sym::diagnostic_on_move)), + (sym::on_const, Some(sym::diagnostic_on_const)), + (sym::on_unknown, Some(sym::diagnostic_on_unknown)), + (sym::on_unmatched_args, Some(sym::diagnostic_on_unmatched_args)), + (sym::on_type_error, Some(sym::diagnostic_on_type_error)), + (sym::opaque, Some(sym::diagnostic_opaque)), + ]; + // No need to emit a lint if features aren't available. + let Some(features) = self.features else { return }; + let span = segment.span(); + let candidates = DIAGNOSTIC_ATTRIBUTES + .iter() + .filter_map(|(attr, feature)| { + feature.is_none_or(|f| features.enabled(f)).then_some(*attr) + }) + .collect::>(); + + let typo = find_best_match_for_name(&candidates, segment.ident.name, None) + .map(|typo_name| diagnostics::UnknownDiagnosticAttributeTypo { span, typo_name }); + emit_lint( + LintId::of(UNKNOWN_DIAGNOSTIC_ATTRIBUTES), + span.into(), + EmitAttribute(Box::new(move |dcx, level, _| { + diagnostics::UnknownDiagnosticAttribute { typo }.into_diag(dcx, level) + })), + ) + } +} + +#[rustc_macro_transparency = "transparent"] +macro gate_diagnostic_attr($feature:ident) {{ + if let Some(features) = cx.features_option() + && !features.$feature() + { + args.ignore_args(); + let nightly_build = cx.sess.is_nightly_build(); + let span = cx.attr_span; + cx.emit_lint( + rustc_lint_defs::builtin::UNKNOWN_DIAGNOSTIC_ATTRIBUTES, + $crate::diagnostics::UnstableDiagnosticAttribute { + feature: sym::$feature, + nightly_build, + }, + span, + ); + return; + } +}} + #[derive(Copy, Clone)] pub(crate) enum Mode { /// `#[rustc_on_unimplemented]` diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs index 20c0ac7cc8554..f92a7694ec357 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs @@ -13,13 +13,9 @@ impl AttributeParser for OnConstParser { const ATTRIBUTES: AcceptMapping = &[( &[sym::diagnostic, sym::on_const], template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), - AttributeStability::Stable, // Unstable, stability checked manually in the parser + AttributeStability::Stable, // Unstable, stability checked manually below |this, cx, args| { - if !cx.features().diagnostic_on_const() { - // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs - args.ignore_args(); - return; - } + gate_diagnostic_attr!(diagnostic_on_const); let path_span = cx.attr_path.span; this.path_span = Some(path_span); diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs index 1c8b3418fa746..dcba5d5b301a6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs @@ -4,8 +4,6 @@ use rustc_span::sym; use crate::attributes::diagnostic::*; use crate::attributes::prelude::*; -use crate::context::AcceptContext; -use crate::parser::ArgParser; use crate::target_checking::AllowedTargets; use crate::template; @@ -15,31 +13,23 @@ pub(crate) struct OnMoveParser { directive: Option<(Span, Directive)>, } -impl OnMoveParser { - fn parse<'sess>(&mut self, cx: &mut AcceptContext<'_, 'sess>, args: &ArgParser, mode: Mode) { - if !cx.features().diagnostic_on_move() { - // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs - args.ignore_args(); - return; - } - - let span = cx.attr_span; - self.span = Some(span); - - let Some(items) = parse_list(cx, args, mode) else { return }; - - if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) { - merge_directives(cx, &mut self.directive, (span, directive)); - } - } -} impl AttributeParser for OnMoveParser { const ATTRIBUTES: AcceptMapping = &[( &[sym::diagnostic, sym::on_move], template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), - AttributeStability::Stable, // Unstable, stability checked manually in the parser + AttributeStability::Stable, // Unstable, stability checked manually below |this, cx, args| { - this.parse(cx, args, Mode::DiagnosticOnMove); + gate_diagnostic_attr!(diagnostic_on_move); + + let span = cx.attr_span; + this.span = Some(span); + let mode = Mode::DiagnosticOnMove; + + let Some(items) = parse_list(cx, args, mode) else { return }; + + if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) { + merge_directives(cx, &mut this.directive, (span, directive)); + } }, )]; diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs index 38c1f9ab6c945..1bdde3af7f0eb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs @@ -4,8 +4,6 @@ use rustc_span::sym; use crate::attributes::AttributeStability; use crate::attributes::diagnostic::*; use crate::attributes::prelude::*; -use crate::context::AcceptContext; -use crate::parser::ArgParser; use crate::target_checking::AllowedTargets; use crate::template; @@ -15,32 +13,22 @@ pub(crate) struct OnTypeErrorParser { directive: Option<(Span, Directive)>, } -impl OnTypeErrorParser { - fn parse<'sess>(&mut self, cx: &mut AcceptContext<'_, 'sess>, args: &ArgParser, mode: Mode) { - if !cx.features().diagnostic_on_type_error() { - // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs - args.ignore_args(); - return; - } - - let span = cx.attr_span; - self.span = Some(span); - - let Some(items) = parse_list(cx, args, mode) else { return }; - - if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) { - merge_directives(cx, &mut self.directive, (span, directive)); - } - } -} - impl AttributeParser for OnTypeErrorParser { const ATTRIBUTES: AcceptMapping = &[( &[sym::diagnostic, sym::on_type_error], template!(List: &[r#"note = "...""#]), - AttributeStability::Stable, + AttributeStability::Stable, // Unstable, stability checked manually below |this, cx, args| { - this.parse(cx, args, Mode::DiagnosticOnTypeError); + gate_diagnostic_attr!(diagnostic_on_type_error); + + let span = cx.attr_span; + this.span = Some(span); + let mode = Mode::DiagnosticOnTypeError; + let Some(items) = parse_list(cx, args, mode) else { return }; + + if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) { + merge_directives(cx, &mut this.directive, (span, directive)); + } }, )]; diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs index bfa26d993b17e..029d971910e5e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs @@ -10,33 +10,23 @@ pub(crate) struct OnUnknownParser { directive: Option<(Span, Directive)>, } -impl OnUnknownParser { - fn parse<'sess>(&mut self, cx: &mut AcceptContext<'_, 'sess>, args: &ArgParser, mode: Mode) { - if let Some(features) = cx.features - && !features.diagnostic_on_unknown() - { - // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs - args.ignore_args(); - return; - } - let span = cx.attr_span; - self.span = Some(span); - - let Some(items) = parse_list(cx, args, mode) else { return }; - - if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) { - merge_directives(cx, &mut self.directive, (span, directive)); - }; - } -} - impl AttributeParser for OnUnknownParser { const ATTRIBUTES: AcceptMapping = &[( &[sym::diagnostic, sym::on_unknown], template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), - AttributeStability::Stable, // Unstable, stability checked manually in the parser + AttributeStability::Stable, // Unstable, stability checked manually below |this, cx, args| { - this.parse(cx, args, Mode::DiagnosticOnUnknown); + gate_diagnostic_attr!(diagnostic_on_unknown); + + let span = cx.attr_span; + this.span = Some(span); + let mode = Mode::DiagnosticOnUnknown; + + let Some(items) = parse_list(cx, args, mode) else { return }; + + if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) { + merge_directives(cx, &mut this.directive, (span, directive)); + }; }, )]; // "Allowed" for all targets, but noop for all but use statements. diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs index df8cee63506cc..41ed6df43063e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatched_args.rs @@ -14,13 +14,9 @@ impl AttributeParser for OnUnmatchedArgsParser { const ATTRIBUTES: AcceptMapping = &[( &[sym::diagnostic, sym::on_unmatched_args], template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), - AttributeStability::Stable, // Unstable, stability checked manually in the parser + AttributeStability::Stable, // Unstable, stability checked manually below |this, cx, args| { - if !cx.features().diagnostic_on_unmatched_args() { - // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs - args.ignore_args(); - return; - } + gate_diagnostic_attr!(diagnostic_on_unmatched_args); let span = cx.attr_span; this.span = Some(span); diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs index 64c5ed704f8c5..0864b76f0e030 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs @@ -4,6 +4,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES; use rustc_span::{Span, sym}; +use crate::attributes::diagnostic::gate_diagnostic_attr; use crate::attributes::{AcceptMapping, AttributeParser}; use crate::context::{AcceptContext, FinalizeContext}; use crate::diagnostics::OpaqueDoesNotExpectArgs; @@ -22,11 +23,9 @@ impl AttributeParser for OpaqueParser { ( &[sym::diagnostic, sym::opaque], template!(Word), - AttributeStability::Stable, // Unstable, stability checked manually in the parser + AttributeStability::Stable, // Unstable, stability checked manually below |this, cx, args| { - if !cx.features().diagnostic_opaque() { - return; - } + gate_diagnostic_attr!(diagnostic_opaque); this.parse(cx, args); }, ), diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index f9ebd78580b4c..1e76ab44826a9 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -848,3 +848,32 @@ pub(crate) struct ToolReserved { pub(crate) span: Span, pub(crate) tool: Ident, } + +#[derive(Diagnostic)] +#[diag("unknown diagnostic attribute")] +pub(crate) struct UnknownDiagnosticAttribute { + #[subdiagnostic] + pub typo: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "an attribute with a similar name exists", + style = "verbose", + code = "{typo_name}", + applicability = "machine-applicable" +)] +pub(crate) struct UnknownDiagnosticAttributeTypo { + #[primary_span] + pub span: Span, + pub typo_name: Symbol, +} + +#[derive(Diagnostic)] +#[diag("unknown diagnostic attribute")] +pub(crate) struct UnstableDiagnosticAttribute { + #[note("this is an experimental diagnostic attribute")] + #[help("add `#![feature({$feature})]` to the crate attributes to enable")] + pub nightly_build: bool, + pub feature: Symbol, +} diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index cea549e310476..73108e42d5ab5 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -292,6 +292,7 @@ impl<'sess> AttributeParser<'sess> { self.sess } + #[track_caller] pub(crate) fn features(&self) -> &'sess Features { self.features.expect("features not available at this point in the compiler") } @@ -451,6 +452,8 @@ impl<'sess> AttributeParser<'sess> { if !cx.shared.has_lint_been_emitted.load(Ordering::Relaxed) { cx.shared.cx.check_args_used(attr, &args) } + } else if let [sym::diagnostic, _unknown, ..] = &*parts { + self.unknown_diagnostic_attr(&n.item.path.segments[1], &mut emit_lint); } else { let attr = AttrItem { path: attr_path.clone(), diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 1b58a9aae5abe..bcdb401bc08c3 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -87,9 +87,11 @@ //! [`rustc_passes::check_attr`]: ../rustc_passes/check_attr/index.html // tidy-alphabetical-start +#![expect(internal_features, reason = "rustc_attrs")] #![feature(decl_macro)] #![feature(deref_patterns)] #![feature(iter_intersperse)] +#![feature(rustc_attrs)] #![feature(try_blocks)] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 76587ba9f0ead..5f1fc8ba90d5e 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -315,6 +315,7 @@ impl MetaItemOrLitParser { /// `= value` part /// /// The syntax of `MetaItems` can be found at +#[derive(Debug)] pub struct MetaItemParser { path: OwnedPathParser, args: ArgParser, @@ -325,15 +326,6 @@ pub struct MetaItemParser { args_checked: AtomicBool, } -impl Debug for MetaItemParser { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("MetaItemParser") - .field("path", &self.path) - .field("args", &self.args) - .finish() - } -} - impl MetaItemParser { /// For a single-segment meta item, returns its name; otherwise, returns `None`. pub fn ident(&self) -> Option { @@ -385,23 +377,13 @@ impl MetaItemParser { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct NameValueParser { pub eq_span: Span, value: MetaItemLit, pub value_span: Span, } -impl Debug for NameValueParser { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("NameValueParser") - .field("eq_span", &self.eq_span) - .field("value", &self.value) - .field("value_span", &self.value_span) - .finish() - } -} - impl NameValueParser { pub fn value_as_lit(&self) -> &MetaItemLit { &self.value diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 49307e78fbb14..d40f611150233 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1881,6 +1881,14 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { issued_borrow.borrowed_place, &issued_spans, ); + self.explain_iterator_invalidation_in_for_loop_if_applicable( + &mut err, + &issued_spans, + place, + issued_borrow.borrowed_place, + issued_borrow.kind, + span, + ); err } @@ -1904,6 +1912,14 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { span, issued_span, ); + self.explain_iterator_invalidation_in_for_loop_if_applicable( + &mut err, + &issued_spans, + place, + issued_borrow.borrowed_place, + issued_borrow.kind, + span, + ); self.suggest_using_closure_argument_instead_of_capture( &mut err, issued_borrow.borrowed_place, @@ -2619,6 +2635,50 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { } } + /// Explain iterator invalidation when mutating a collection in a for loop. + /// + /// For example: + /// ```compile_fail + /// let mut values = vec![1, 2, 3]; + /// for value in &values { + /// values.push(4); + /// } + /// ``` + fn explain_iterator_invalidation_in_for_loop_if_applicable( + &self, + err: &mut Diag<'_>, + issued_spans: &UseSpans<'tcx>, + place: Place<'tcx>, + borrowed_place: Place<'tcx>, + borrow_kind: BorrowKind, + gen_span: Span, + ) { + let issue_span = issued_spans.args_or_use(); + let tcx = self.infcx.tcx; + + let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return }; + + if let Some(for_span) = find_for_loop_span(tcx, body_id, issue_span) + && place.local == borrowed_place.local + && for_span.contains(gen_span) + { + let place_desc = self.describe_any_place(place.as_ref()); + let borrow_kind_str = + if matches!(borrow_kind, BorrowKind::Mut { .. }) { "mutably" } else { "immutably" }; + err.span_label( + for_span, + format!( + "this for loop borrows {place_desc} {borrow_kind_str}, \ + preventing mutation within its body" + ), + ); + err.help( + "consider using an index-based loop instead, or collecting \ + modifications into a separate collection", + ); + } + } + /// Suggest using closure argument instead of capture. /// /// For example: @@ -4658,6 +4718,38 @@ enum AnnotatedBorrowFnSignature<'tcx> { }, } +/// Find the `Match` expression desugared from a for loop, whose +/// `IntoIter::into_iter` argument contains `issue_span`. +/// Returns the for-loop match expression span. +fn find_for_loop_span<'hir>( + tcx: TyCtxt<'hir>, + body_id: hir::BodyId, + issue_span: Span, +) -> Option { + struct ExprFinder<'hir> { + tcx: TyCtxt<'hir>, + issue_span: Span, + result: Option, + } + impl<'hir> Visitor<'hir> for ExprFinder<'hir> { + fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { + if let hir::ExprKind::Match(scrutinee, _, hir::MatchSource::ForLoopDesugar) = ex.kind + && let hir::ExprKind::Call(path, [arg]) = scrutinee.kind + && let hir::ExprKind::Path(qpath) = path.kind + && self.tcx.qpath_is_lang_item(qpath, LangItem::IntoIterIntoIter) + && arg.span.contains(self.issue_span) + { + self.result = Some(ex.span); + return; + } + hir::intravisit::walk_expr(self, ex); + } + } + let mut finder = ExprFinder { tcx, issue_span, result: None }; + finder.visit_expr(tcx.hir_body(body_id).value); + finder.result +} + impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { /// Annotate the provided diagnostic with information about borrow from the fn signature that /// helps explain. diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 2d4d98d812c65..3479224cc5546 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -26,8 +26,8 @@ use rustc_macros::extension; use rustc_middle::mir::RETURN_PLACE; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, RegionExt, RegionVid, - Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, + self, BoundVariableKind, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, + List, RegionExt, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_span::{ErrorGuaranteed, kw, sym}; @@ -134,6 +134,231 @@ pub(crate) enum DefiningTy<'tcx> { } impl<'tcx> DefiningTy<'tcx> { + #[instrument(level = "debug", skip(tcx), ret)] + fn new(tcx: TyCtxt<'tcx>, body_def_id: LocalDefId) -> DefiningTy<'tcx> { + match tcx.hir_body_owner_kind(body_def_id) { + BodyOwnerKind::Closure | BodyOwnerKind::Fn => { + let defining_ty = tcx.type_of(body_def_id).instantiate_identity().skip_norm_wip(); + match *defining_ty.kind() { + ty::Closure(def_id, args) => DefiningTy::Closure(def_id, args), + ty::Coroutine(def_id, args) => DefiningTy::Coroutine(def_id, args), + ty::CoroutineClosure(def_id, args) => { + DefiningTy::CoroutineClosure(def_id, args) + } + ty::FnDef(def_id, args) => { + DefiningTy::FnDef(def_id, args.no_bound_vars().unwrap()) + } + _ => span_bug!( + tcx.def_span(body_def_id), + "expected defining type for `{body_def_id:?}`: `{defining_ty:?}`", + ), + } + } + + BodyOwnerKind::Const { inline: true } => { + // This is required for `AscribeUserType` canonical query, which will call + // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes + // into borrowck, which is ICE #78174. + // + // As a workaround, inline consts have an additional generic param (`ty` + // below), so that `type_of(inline_const_def_id).substs(substs)` uses the + // proper type with NLL infer vars. + // + // Fetch the actual type from MIR, as `type_of` returns something useless + // like ``. + let body = tcx.mir_promoted(body_def_id).0.borrow(); + let ty = body.local_decls[RETURN_PLACE].ty; + let typeck_root_def_id = tcx.typeck_root_def_id(body_def_id.to_def_id()); + let parent_args = GenericArgs::identity_for_item(tcx, typeck_root_def_id); + let args = InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty }).args; + DefiningTy::InlineConst(body_def_id.to_def_id(), args) + } + + BodyOwnerKind::Const { inline: false } | BodyOwnerKind::Static(..) => { + let args = GenericArgs::identity_for_item(tcx, body_def_id.to_def_id()); + DefiningTy::Const(body_def_id.to_def_id(), args) + } + + BodyOwnerKind::GlobalAsm => DefiningTy::GlobalAsm(body_def_id.to_def_id()), + } + } + + /// The bound variables for a given defining type. This differs from their usual bound vars + /// in that closures and coroutine closures have an additional `'env`, while C-variadic + /// functions have an additional region for their implicit `VaList` input. + pub(crate) fn bound_vars(self, tcx: TyCtxt<'tcx>) -> &'tcx List> { + match self { + DefiningTy::Closure(_, args) => { + let closure_sig = args.as_closure().sig(); + let inputs_and_output = closure_sig.inputs_and_output(); + tcx.mk_bound_variable_kinds_from_iter(inputs_and_output.bound_vars().iter().chain( + iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv)), + )) + } + + DefiningTy::CoroutineClosure(_, args) => { + let closure_sig = args.as_coroutine_closure().coroutine_closure_sig(); + tcx.mk_bound_variable_kinds_from_iter(closure_sig.bound_vars().iter().chain( + iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv)), + )) + } + + DefiningTy::FnDef(def_id, _) => { + let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); + if sig.skip_binder().c_variadic() { + // FIXME(#160495): Don't use an anonymous region here + tcx.mk_bound_variable_kinds_from_iter(sig.bound_vars().iter().chain( + iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon)), + )) + } else { + sig.bound_vars() + } + } + + DefiningTy::Coroutine(..) + | DefiningTy::Const(..) + | DefiningTy::InlineConst(..) + | DefiningTy::GlobalAsm(..) => ty::List::empty(), + } + } + + #[instrument(level = "debug", skip(tcx), ret)] + fn inputs_and_output(self, tcx: TyCtxt<'tcx>) -> ty::Binder<'tcx, &'tcx ty::List>> { + match self { + DefiningTy::Closure(def_id, args) => { + let closure_sig = args.as_closure().sig(); + let inputs_and_output = closure_sig.inputs_and_output(); + let bound_vars = self.bound_vars(tcx); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BoundRegionKind::ClosureEnv, + }; + let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + let closure_ty = tcx.closure_env_ty( + Ty::new_closure(tcx, def_id, args), + args.as_closure().kind(), + env_region, + ); + + // The "inputs" of the closure in the + // signature appear as a tuple. The MIR side + // flattens this tuple. + let (&output, tuplized_inputs) = + inputs_and_output.skip_binder().split_last().unwrap(); + assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs"); + let &ty::Tuple(inputs) = tuplized_inputs[0].kind() else { + bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]); + }; + + ty::Binder::bind_with_vars( + tcx.mk_type_list_from_iter( + iter::once(closure_ty).chain(inputs).chain(iter::once(output)), + ), + bound_vars, + ) + } + + DefiningTy::Coroutine(def_id, args) => { + let resume_ty = args.as_coroutine().resume_ty(); + let output = args.as_coroutine().return_ty(); + let coroutine_ty = Ty::new_coroutine(tcx, def_id, args); + let inputs_and_output = tcx.mk_type_list(&[coroutine_ty, resume_ty, output]); + ty::Binder::dummy(inputs_and_output) + } + + // Construct the signature of the CoroutineClosure for the purposes of borrowck. + // This is pretty straightforward -- we: + // 1. first grab the `coroutine_closure_sig`, + // 2. compute the self type (`&`/`&mut`/no borrow), + // 3. flatten the tupled_input_tys, + // 4. construct the correct generator type to return with + // `CoroutineClosureSignature::to_coroutine_given_kind_and_upvars`. + // Then we wrap it all up into a list of inputs and output. + DefiningTy::CoroutineClosure(def_id, args) => { + let closure_sig = args.as_coroutine_closure().coroutine_closure_sig(); + let bound_vars = self.bound_vars(tcx); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BoundRegionKind::ClosureEnv, + }; + let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + let closure_kind = args.as_coroutine_closure().kind(); + + let closure_ty = tcx.closure_env_ty( + Ty::new_coroutine_closure(tcx, def_id, args), + closure_kind, + env_region, + ); + + let inputs = closure_sig.skip_binder().tupled_inputs_ty.tuple_fields(); + let output = closure_sig.skip_binder().to_coroutine_given_kind_and_upvars( + tcx, + args.as_coroutine_closure().parent_args(), + tcx.coroutine_for_closure(def_id), + closure_kind, + env_region, + args.as_coroutine_closure().tupled_upvars_ty(), + args.as_coroutine_closure().coroutine_captures_by_ref_ty(), + ); + + ty::Binder::bind_with_vars( + tcx.mk_type_list_from_iter( + iter::once(closure_ty).chain(inputs).chain(iter::once(output)), + ), + bound_vars, + ) + } + + DefiningTy::FnDef(def_id, _) => { + let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); + let inputs_and_output = sig.inputs_and_output(); + + // C-variadic fns also have a `VaList` input that's not listed in the signature + // (as it's created inside the body itself, not passed in from outside). + if tcx.fn_sig(def_id).skip_binder().c_variadic() { + let va_list_did = tcx.require_lang_item(LangItem::VaList, tcx.def_span(def_id)); + + let bound_vars = self.bound_vars(tcx); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BoundRegionKind::Anon, + }; + let region = ty::Region::new_bound(tcx, ty::INNERMOST, br); + let va_list_ty = + tcx.type_of(va_list_did).instantiate(tcx, &[region.into()]).skip_norm_wip(); + + // The signature needs to follow the order [input_tys, va_list_ty, output_ty] + let (output_ty, input_tys) = + inputs_and_output.skip_binder().split_last().unwrap(); + return ty::Binder::bind_with_vars( + tcx.mk_type_list_from_iter( + input_tys.iter().copied().chain([va_list_ty, *output_ty]), + ), + bound_vars, + ); + } + + inputs_and_output + } + + DefiningTy::Const(def_id, _) => { + // For a constant body, there are no inputs, and one + // "output" (the type of the constant). + let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip(); + ty::Binder::dummy(tcx.mk_type_list(&[ty])) + } + + DefiningTy::InlineConst(_def_id, args) => { + let ty = args.as_inline_const().ty(); + ty::Binder::dummy(tcx.mk_type_list(&[ty])) + } + + DefiningTy::GlobalAsm(def_id) => ty::Binder::dummy( + tcx.mk_type_list(&[tcx.type_of(def_id).instantiate_identity().skip_norm_wip()]), + ), + } + } + /// Returns a list of all the upvar types for this MIR. If this is /// not a closure or coroutine, there are no upvars, and hence it /// will be an empty list. The order of types in this list will @@ -488,7 +713,9 @@ impl<'tcx> UniversalRegionsBuilder<'_, 'tcx> { } else { // If this is a closure, coroutine, or inline-const, then the late-bound regions from the enclosing // function/closures are actually external regions to us. For example, here, 'a is not local - // to the closure c (although it is local to the fn foo): + // to the closure c (although it is local to the fn foo). We need to add them as they could be + // explicitly named in this body: + // // fn foo<'a>() { // let c = || { let x: &'a u32 = ...; } // } @@ -518,8 +745,9 @@ impl<'tcx> UniversalRegionsBuilder<'_, 'tcx> { // on its signature are local. // // We manually loop over `bound_inputs_and_output` instead of using - // `for_each_late_bound_region_in_item` as we may need to add the otherwise - // implicit `ClosureEnv` region. + // `for_each_late_bound_region_in_item` as both closures and function + // definitions have implicit late bound regions. Closures have a `'env` + // regions while c-variadic function definitions have a `&VaList` argument. let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty); for (idx, bound_var) in bound_inputs_and_output.bound_vars().iter().enumerate() { if let ty::BoundVariableKind::Region(kind) = bound_var { @@ -581,82 +809,23 @@ impl<'tcx> UniversalRegionsBuilder<'_, 'tcx> { } } - /// Returns the "defining type" of the current MIR; - /// see `DefiningTy` for details. + /// Returns the "defining type" of the current MIR; see `DefiningTy` for details. fn defining_ty(&self) -> DefiningTy<'tcx> { - let tcx = self.infcx.tcx; - - match tcx.hir_body_owner_kind(self.mir_def) { - BodyOwnerKind::Closure | BodyOwnerKind::Fn => { - let defining_ty = tcx.type_of(self.mir_def).instantiate_identity().skip_norm_wip(); - - debug!("defining_ty (pre-replacement): {:?}", defining_ty); - - let defining_ty = self.infcx.replace_free_regions_with_nll_infer_vars( - NllRegionVariableOrigin::FreeRegion, - defining_ty, - ); - - match *defining_ty.kind() { - ty::Closure(def_id, args) => DefiningTy::Closure(def_id, args), - ty::Coroutine(def_id, args) => DefiningTy::Coroutine(def_id, args), - ty::CoroutineClosure(def_id, args) => { - DefiningTy::CoroutineClosure(def_id, args) - } - ty::FnDef(def_id, args) => { - DefiningTy::FnDef(def_id, args.no_bound_vars().unwrap()) - } - _ => span_bug!( - tcx.def_span(self.mir_def), - "expected defining type for `{:?}`: `{:?}`", - self.mir_def, - defining_ty - ), - } - } - - BodyOwnerKind::Const { .. } | BodyOwnerKind::Static(..) => { - match tcx.def_kind(self.mir_def) { - DefKind::AnonConst - if tcx.anon_const_kind(self.mir_def) - == ty::AnonConstKind::NonTypeSystemInline => - { - // This is required for `AscribeUserType` canonical query, which will call - // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes - // into borrowck, which is ICE #78174. - // - // As a workaround, inline consts have an additional generic param (`ty` - // below), so that `type_of(inline_const_def_id).substs(substs)` uses the - // proper type with NLL infer vars. - // - // Fetch the actual type from MIR, as `type_of` returns something useless - // like ``. - let body = tcx.mir_promoted(self.mir_def).0.borrow(); - let ty = body.local_decls[RETURN_PLACE].ty; - let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.to_def_id()); - let parent_args = GenericArgs::identity_for_item(tcx, typeck_root_def_id); - let args = - InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty }) - .args; - let args = self.infcx.replace_free_regions_with_nll_infer_vars( - NllRegionVariableOrigin::FreeRegion, - args, - ); - DefiningTy::InlineConst(self.mir_def.to_def_id(), args) - } - _ => { - let identity_args = - GenericArgs::identity_for_item(tcx, self.mir_def.to_def_id()); - let args = self.infcx.replace_free_regions_with_nll_infer_vars( - NllRegionVariableOrigin::FreeRegion, - identity_args, - ); - DefiningTy::Const(self.mir_def.to_def_id(), args) - } - } + let defining_ty = DefiningTy::new(self.infcx.tcx, self.mir_def); + let f = |args| { + let fr = NllRegionVariableOrigin::FreeRegion; + self.infcx.replace_free_regions_with_nll_infer_vars(fr, args) + }; + match defining_ty { + DefiningTy::Closure(def_id, args) => DefiningTy::Closure(def_id, f(args)), + DefiningTy::Coroutine(def_id, args) => DefiningTy::Coroutine(def_id, f(args)), + DefiningTy::CoroutineClosure(def_id, args) => { + DefiningTy::CoroutineClosure(def_id, f(args)) } - - BodyOwnerKind::GlobalAsm => DefiningTy::GlobalAsm(self.mir_def.to_def_id()), + DefiningTy::FnDef(def_id, args) => DefiningTy::FnDef(def_id, f(args)), + DefiningTy::Const(def_id, args) => DefiningTy::Const(def_id, f(args)), + DefiningTy::InlineConst(def_id, args) => DefiningTy::InlineConst(def_id, f(args)), + DefiningTy::GlobalAsm(def_id) => DefiningTy::GlobalAsm(def_id), } } @@ -694,163 +863,8 @@ impl<'tcx> UniversalRegionsBuilder<'_, 'tcx> { defining_ty: DefiningTy<'tcx>, ) -> ty::Binder<'tcx, &'tcx ty::List>> { let tcx = self.infcx.tcx; - - let inputs_and_output = match defining_ty { - DefiningTy::Closure(def_id, args) => { - assert_eq!(self.mir_def.to_def_id(), def_id); - let closure_sig = args.as_closure().sig(); - let inputs_and_output = closure_sig.inputs_and_output(); - let bound_vars = tcx.mk_bound_variable_kinds_from_iter( - inputs_and_output.bound_vars().iter().chain(iter::once( - ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), - )), - ); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); - let closure_ty = tcx.closure_env_ty( - Ty::new_closure(tcx, def_id, args), - args.as_closure().kind(), - env_region, - ); - - // The "inputs" of the closure in the - // signature appear as a tuple. The MIR side - // flattens this tuple. - let (&output, tuplized_inputs) = - inputs_and_output.skip_binder().split_last().unwrap(); - assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs"); - let &ty::Tuple(inputs) = tuplized_inputs[0].kind() else { - bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]); - }; - - ty::Binder::bind_with_vars( - tcx.mk_type_list_from_iter( - iter::once(closure_ty).chain(inputs).chain(iter::once(output)), - ), - bound_vars, - ) - } - - DefiningTy::Coroutine(def_id, args) => { - assert_eq!(self.mir_def.to_def_id(), def_id); - let resume_ty = args.as_coroutine().resume_ty(); - let output = args.as_coroutine().return_ty(); - let coroutine_ty = Ty::new_coroutine(tcx, def_id, args); - let inputs_and_output = - self.infcx.tcx.mk_type_list(&[coroutine_ty, resume_ty, output]); - ty::Binder::dummy(inputs_and_output) - } - - // Construct the signature of the CoroutineClosure for the purposes of borrowck. - // This is pretty straightforward -- we: - // 1. first grab the `coroutine_closure_sig`, - // 2. compute the self type (`&`/`&mut`/no borrow), - // 3. flatten the tupled_input_tys, - // 4. construct the correct generator type to return with - // `CoroutineClosureSignature::to_coroutine_given_kind_and_upvars`. - // Then we wrap it all up into a list of inputs and output. - DefiningTy::CoroutineClosure(def_id, args) => { - assert_eq!(self.mir_def.to_def_id(), def_id); - let closure_sig = args.as_coroutine_closure().coroutine_closure_sig(); - let bound_vars = - tcx.mk_bound_variable_kinds_from_iter(closure_sig.bound_vars().iter().chain( - iter::once(ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv)), - )); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BoundRegionKind::ClosureEnv, - }; - let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); - let closure_kind = args.as_coroutine_closure().kind(); - - let closure_ty = tcx.closure_env_ty( - Ty::new_coroutine_closure(tcx, def_id, args), - closure_kind, - env_region, - ); - - let inputs = closure_sig.skip_binder().tupled_inputs_ty.tuple_fields(); - let output = closure_sig.skip_binder().to_coroutine_given_kind_and_upvars( - tcx, - args.as_coroutine_closure().parent_args(), - tcx.coroutine_for_closure(def_id), - closure_kind, - env_region, - args.as_coroutine_closure().tupled_upvars_ty(), - args.as_coroutine_closure().coroutine_captures_by_ref_ty(), - ); - - ty::Binder::bind_with_vars( - tcx.mk_type_list_from_iter( - iter::once(closure_ty).chain(inputs).chain(iter::once(output)), - ), - bound_vars, - ) - } - - DefiningTy::FnDef(def_id, _) => { - let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); - let sig = indices.fold_to_region_vids(tcx, sig); - let inputs_and_output = sig.inputs_and_output(); - - // C-variadic fns also have a `VaList` input that's not listed in the signature - // (as it's created inside the body itself, not passed in from outside). - if self.infcx.tcx.fn_sig(def_id).skip_binder().c_variadic() { - let va_list_did = self - .infcx - .tcx - .require_lang_item(LangItem::VaList, self.infcx.tcx.def_span(self.mir_def)); - - let reg_vid = self - .infcx - .next_nll_region_var(NllRegionVariableOrigin::FreeRegion, || { - RegionCtxt::Free(sym::c_dash_variadic) - }) - .as_var(); - - let region = ty::Region::new_var(self.infcx.tcx, reg_vid); - let va_list_ty = self - .infcx - .tcx - .type_of(va_list_did) - .instantiate(self.infcx.tcx, &[region.into()]) - .skip_norm_wip(); - - // The signature needs to follow the order [input_tys, va_list_ty, output_ty] - return inputs_and_output.map_bound(|tys| { - let (output_ty, input_tys) = tys.split_last().unwrap(); - tcx.mk_type_list_from_iter( - input_tys.iter().copied().chain([va_list_ty, *output_ty]), - ) - }); - } - - inputs_and_output - } - - DefiningTy::Const(def_id, _) => { - // For a constant body, there are no inputs, and one - // "output" (the type of the constant). - assert_eq!(self.mir_def.to_def_id(), def_id); - let ty = tcx.type_of(self.mir_def).instantiate_identity().skip_norm_wip(); - - let ty = indices.fold_to_region_vids(tcx, ty); - ty::Binder::dummy(tcx.mk_type_list(&[ty])) - } - - DefiningTy::InlineConst(def_id, args) => { - assert_eq!(self.mir_def.to_def_id(), def_id); - let ty = args.as_inline_const().ty(); - ty::Binder::dummy(tcx.mk_type_list(&[ty])) - } - - DefiningTy::GlobalAsm(def_id) => ty::Binder::dummy( - tcx.mk_type_list(&[tcx.type_of(def_id).instantiate_identity().skip_norm_wip()]), - ), - }; + let inputs_and_output = defining_ty.inputs_and_output(tcx); + let inputs_and_output = indices.fold_to_region_vids(tcx, inputs_and_output); // FIXME(#129952): We probably want a more principled approach here. if let Err(e) = inputs_and_output.error_reported() { diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 03fd11afa3f10..0b8eb75972ec0 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -404,7 +404,7 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { let abi_clobber = InlineAsmClobberAbi::parse( self.arch, &self.tcx.sess.target, - &self.tcx.sess.unstable_target_features, + &self.tcx.sess.internal_target_features, sym::C, ) .unwrap() diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 8ee0e71d82dec..8b0ca770ec067 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -39,6 +39,7 @@ use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::settings::{self, Configurable}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig, back}; +use rustc_data_structures::unord::UnordSet; use rustc_log::tracing::info; use rustc_middle::dep_graph::WorkProductMap; use rustc_session::config::{NATIVE_CPU, OutputFilenames}; @@ -170,8 +171,6 @@ impl CodegenBackend for CraneliftCodegenBackend { }, _ => vec![], }; - // FIXME do `unstable_target_features` properly - let unstable_target_features = target_features.clone(); // FIXME(f16_f128): `rustc_codegen_llvm` currently disables support on Windows GNU // targets due to GCC using a different ABI than LLVM. Therefore `f16` and `f128` @@ -186,8 +185,7 @@ impl CodegenBackend for CraneliftCodegenBackend { let has_reliable_f128_math = has_reliable_f16_f128 && sess.target.env == Env::Gnu; TargetConfig { - target_features, - unstable_target_features, + internal_target_features: UnordSet::from_iter(target_features), // `rustc_codegen_cranelift` polyfills functionality not yet // available in Cranelift. has_reliable_f16: has_reliable_f16_f128, diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index c7c0687c4f156..cbc7db8e9e23f 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -85,7 +85,7 @@ use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryFn, ThinLtoInput, }; use rustc_codegen_ssa::base::codegen_crate; -use rustc_codegen_ssa::target_features::cfg_target_feature; +use rustc_codegen_ssa::target_features::internal_target_features; use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods}; use rustc_codegen_ssa::{CompiledModule, CompiledModules, CrateInfo, ModuleCodegen, TargetConfig}; use rustc_data_structures::profiling::SelfProfilerRef; @@ -532,7 +532,7 @@ fn to_gcc_opt_level(optlevel: Option) -> OptimizationLevel { /// Returns the features that should be set in `cfg(target_feature)`. fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig { - let (unstable_target_features, target_features) = cfg_target_feature( + let internal_target_features = internal_target_features( sess, |feature| to_gcc_features(sess, feature), |feature| { @@ -556,8 +556,7 @@ fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig let has_reliable_f128 = target_info.supports_target_dependent_type(CType::Float128); TargetConfig { - target_features, - unstable_target_features, + internal_target_features, // There are no known bugs with GCC support for f16 or f128 has_reliable_f16, has_reliable_f16_math: has_reliable_f16, diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index d2dfa9a45de8b..fba43bba737e0 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -970,14 +970,14 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' Hexagon(HexagonInlineAsmRegClass::vreg) => { // HVX vector register size depends on the HVX mode. // LLVM's "v" constraint requires the exact vector width. - if cx.tcx.sess.unstable_target_features.contains(&sym::hvx_length128b) { + if cx.tcx.sess.internal_target_features.contains(&sym::hvx_length128b) { cx.type_vector(cx.type_i32(), 32) // 1024-bit for 128B mode } else { cx.type_vector(cx.type_i32(), 16) // 512-bit for 64B mode } } Hexagon(HexagonInlineAsmRegClass::vreg_pair) => { - if cx.tcx.sess.unstable_target_features.contains(&sym::hvx_length128b) { + if cx.tcx.sess.internal_target_features.contains(&sym::hvx_length128b) { cx.type_vector(cx.type_i32(), 64) // 2048-bit for 128B mode } else { cx.type_vector(cx.type_i32(), 32) // 1024-bit for 64B mode diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index a2f44757c020a..06dab7f7e46fd 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -393,9 +393,9 @@ fn packed_stack_attr<'ll>( // The backchain and softfloat flags can be set via -Ctarget-features=... // or via #[target_features(enable = ...)] so we have to check both possibilities - let have_backchain = sess.unstable_target_features.contains(&sym::backchain) + let have_backchain = sess.internal_target_features.contains(&sym::backchain) || function_attributes.iter().any(|feature| feature.name == sym::backchain); - let have_softfloat = sess.unstable_target_features.contains(&sym::soft_float) + let have_softfloat = sess.internal_target_features.contains(&sym::soft_float) || function_attributes.iter().any(|feature| feature.name == sym::soft_float); // If both, backchain and packedstack, are enabled LLVM cannot generate valid function entry points diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index edf52e67b434b..400a9ce89df8b 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -213,7 +213,7 @@ pub(crate) fn target_machine_factory( let code_model = to_llvm_code_model(sess.code_model()); // This is used to set cfg_has_threads, so all logic must be in this method. - let singlethread = sess.target.singlethread(&sess.target_features); + let singlethread = sess.target.singlethread(&sess.internal_target_features); let triple = SmallCStr::new(&versioned_llvm_target(sess)); let cpu = SmallCStr::new(llvm_util::target_cpu(sess)); @@ -720,7 +720,11 @@ pub(crate) unsafe fn llvm_optimize( // Here we map the old arguments to the new arguments, with an offset of 1 to make sure // that we don't use the newly added `%dyn_ptr`. unsafe { - llvm::LLVMRustOffloadMapper(old_fn, new_fn, old_args_rebuilt.as_ptr()); + llvm::RustOffloadWrapper::get_instance().llvm_rust_offload_wrapper( + old_fn, + new_fn, + old_args_rebuilt.as_slice(), + ); } llvm::set_linkage(new_fn, llvm::get_linkage(old_fn)); @@ -814,16 +818,16 @@ pub(crate) unsafe fn llvm_optimize( let device_dir = device_path.parent().unwrap(); let device_out = device_dir.join("device.bin"); let device_out_c = path_to_c_string(device_out.as_path()); - unsafe { - // 1) Bundle device module into offload image device.bin (device TM) - let ok = llvm::LLVMRustBundleImages( + // 1) Bundle device module into offload image device.bin (device TM) + let ok = unsafe { + llvm::RustOffloadWrapper::get_instance().llvm_rust_bundle_images( module.module_llvm.llmod(), module.module_llvm.tm.raw(), - device_out_c.as_ptr(), - ); - if !ok || !device_out.exists() { - dcx.emit_err(crate::diagnostics::OffloadBundleImagesFailed); - } + device_out_c.as_c_str(), + ) + }; + if !ok || !device_out.exists() { + dcx.emit_err(crate::diagnostics::OffloadBundleImagesFailed); } } @@ -859,8 +863,10 @@ pub(crate) unsafe fn llvm_optimize( // We create a full clone of our LLVM host module, since we will embed the device IR // into it, and this might break caching or incremental compilation otherwise. let llmod2 = llvm::LLVMCloneModule(module.module_llvm.llmod()); - let ok = - unsafe { llvm::LLVMRustOffloadEmbedBufferInModule(llmod2, device_bin_c.as_ptr()) }; + let ok = unsafe { + llvm::RustOffloadWrapper::get_instance() + .llvm_rust_offload_embed_buffer_in_module(llmod2, device_bin_c.as_c_str()) + }; if !ok { dcx.emit_err(crate::diagnostics::OffloadEmbedFailed); } diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index 0b009321802cf..3d0bb6fcc48fd 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -296,7 +296,7 @@ struct KernelArgsTy { impl KernelArgsTy { const OFFLOAD_VERSION: u64 = 3; - const FLAGS: u64 = 0; + const FLAGS: u64 = 1 << 6; // Enable StrictBlocksAndThreads const TRIPCOUNT: u64 = 0; fn new_decl<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll Type { let kernel_arguments_ty = cx.type_named_struct("struct.__tgt_kernel_arguments"); diff --git a/compiler/rustc_codegen_llvm/src/diagnostics.rs b/compiler/rustc_codegen_llvm/src/diagnostics.rs index ea29683b9d289..54f8ffbb881da 100644 --- a/compiler/rustc_codegen_llvm/src/diagnostics.rs +++ b/compiler/rustc_codegen_llvm/src/diagnostics.rs @@ -60,6 +60,19 @@ pub(crate) struct AutoDiffWithoutLto; #[diag("using the autodiff feature requires -Z autodiff=Enable")] pub(crate) struct AutoDiffWithoutEnable; +#[derive(Diagnostic)] +#[diag("failed to load our rust offload backend: {$err}")] +pub(crate) struct RustOffloadComponentUnavailable { + pub err: String, +} + +#[derive(Diagnostic)] +#[diag("rust offload backend not found in the sysroot: {$err}")] +#[note("it will be distributed via rustup in the future")] +pub(crate) struct RustOffloadComponentMissing { + pub err: String, +} + #[derive(Diagnostic)] #[diag( "using the offload feature requires -Z offload=" diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index db8178700caf1..1f0e583709592 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -373,6 +373,26 @@ impl CodegenBackend for LlvmCodegenBackend { } fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { + use rustc_session::config::Offload; + + if tcx.sess.opts.unstable_opts.offload.contains(&Offload::Device) + || tcx.sess.opts.unstable_opts.offload.iter().any(|o| matches!(o, Offload::Host(_))) + { + match llvm::RustOffloadWrapper::get_or_init(&tcx.sess.opts.sysroot) { + Ok(_) => {} + Err(llvm::RustOffloadLibraryError::NotFound { err }) => { + tcx.sess + .dcx() + .emit_fatal(crate::diagnostics::RustOffloadComponentMissing { err }); + } + Err(llvm::RustOffloadLibraryError::LoadFailed { err }) => { + tcx.sess + .dcx() + .emit_fatal(crate::diagnostics::RustOffloadComponentUnavailable { err }); + } + } + } + Box::new(rustc_codegen_ssa::base::codegen_crate(LlvmCodegenBackend(()), tcx)) } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 4cc5d326bdc9e..1a60b59a93525 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1713,63 +1713,6 @@ unsafe extern "C" { ) -> &'a Value; } -#[cfg(feature = "llvm_offload")] -pub(crate) use self::Offload::*; - -#[cfg(feature = "llvm_offload")] -mod Offload { - use super::*; - unsafe extern "C" { - /// Processes the module and writes it in an offload compatible way into a "device.bin" file. - pub(crate) fn LLVMRustBundleImages<'a>( - M: &'a Module, - TM: &'a TargetMachine, - device_bin: *const c_char, - ) -> bool; - pub(crate) unsafe fn LLVMRustOffloadEmbedBufferInModule<'a>( - _M: &'a Module, - _device_bin: *const c_char, - ) -> bool; - pub(crate) fn LLVMRustOffloadMapper<'a>( - OldFn: &'a Value, - NewFn: &'a Value, - RebuiltArgs: *const &Value, - ); - } -} - -#[cfg(not(feature = "llvm_offload"))] -pub(crate) use self::Offload_fallback::*; - -#[cfg(not(feature = "llvm_offload"))] -mod Offload_fallback { - use super::*; - /// Processes the module and writes it in an offload compatible way into a "device.bin" file. - /// Marked as unsafe to match the real offload wrapper which is unsafe due to FFI. - #[allow(unused_unsafe)] - pub(crate) unsafe fn LLVMRustBundleImages<'a>( - _M: &'a Module, - _TM: &'a TargetMachine, - _device_bin: *const c_char, - ) -> bool { - unimplemented!("This rustc version was not built with LLVM Offload support!"); - } - pub(crate) unsafe fn LLVMRustOffloadEmbedBufferInModule<'a>( - _M: &'a Module, - _device_bin: *const c_char, - ) -> bool { - unimplemented!("This rustc version was not built with LLVM Offload support!"); - } - #[allow(unused_unsafe)] - pub(crate) unsafe fn LLVMRustOffloadMapper<'a>( - _OldFn: &'a Value, - _NewFn: &'a Value, - _RebuiltArgs: *const &Value, - ) { - unimplemented!("This rustc version was not built with LLVM Offload support!"); - } -} - // FFI bindings for `DIBuilder` functions in the LLVM-C API. // Try to keep these in the same order as in `llvm/include/llvm-c/DebugInfo.h`. // diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index a2d17e93b4996..eb7a529c0b198 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -21,8 +21,10 @@ pub(crate) mod diagnostic; pub(crate) mod enzyme_ffi; mod ffi; mod metadata_kind; +pub(crate) mod offload_ffi; pub(crate) use self::enzyme_ffi::*; +pub(crate) use self::offload_ffi::*; impl LLVMRustResult { pub(crate) fn into_result(self) -> Result<(), ()> { diff --git a/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs new file mode 100644 index 0000000000000..46d9320248a9b --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs @@ -0,0 +1,133 @@ +use std::ffi::{CStr, c_char}; +use std::sync::OnceLock; + +use super::ffi::{Module, TargetMachine, Value}; + +type LLVMRustBundleImagesFn = unsafe extern "C" fn(&Module, &TargetMachine, *const c_char) -> bool; +type LLVMRustOffloadEmbedBufferInModuleFn = unsafe extern "C" fn(&Module, *const c_char) -> bool; +type LLVMRustOffloadMapperFn = unsafe extern "C" fn(&Value, &Value, *const &Value); + +use rustc_session::config::host_tuple; +use rustc_session::filesearch; + +use crate::llvm::LLVMRustVersionMajor; + +pub(crate) struct RustOffloadWrapper { + LLVMRustBundleImages: LLVMRustBundleImagesFn, + LLVMRustOffloadEmbedBufferInModule: LLVMRustOffloadEmbedBufferInModuleFn, + LLVMRustOffloadMapper: LLVMRustOffloadMapperFn, + // Keep the dynamic library loaded while the function pointers are used. + _lib: libloading::Library, +} + +#[derive(Debug)] +pub(crate) enum RustOffloadLibraryError { + NotFound { err: String }, + LoadFailed { err: String }, +} + +impl From for RustOffloadLibraryError { + fn from(err: libloading::Error) -> Self { + Self::LoadFailed { err: format!("{err:?}") } + } +} + +static OFFLOAD_INSTANCE: OnceLock = OnceLock::new(); + +impl RustOffloadWrapper { + pub(crate) fn get_or_init( + sysroot: &rustc_session::config::Sysroot, + ) -> Result<&'static RustOffloadWrapper, RustOffloadLibraryError> { + OFFLOAD_INSTANCE.get_or_try_init(|| { + let w = Self::call_dynamic(sysroot)?; + Ok(w) + }) + } + + pub(crate) fn get_instance() -> &'static RustOffloadWrapper { + OFFLOAD_INSTANCE + .get() + .expect("RustOffloadWrapper not initialized. Call get_or_init with sysroot first.") + } + + pub(crate) unsafe fn llvm_rust_bundle_images( + &self, + m: &Module, + tm: &TargetMachine, + c: &CStr, + ) -> bool { + unsafe { (self.LLVMRustBundleImages)(m, tm, c.as_ptr()) } + } + + pub(crate) unsafe fn llvm_rust_offload_embed_buffer_in_module( + &self, + m: &Module, + i: &CStr, + ) -> bool { + unsafe { (self.LLVMRustOffloadEmbedBufferInModule)(m, i.as_ptr()) } + } + + pub(crate) unsafe fn llvm_rust_offload_wrapper(&self, v1: &Value, v2: &Value, vs: &[&Value]) { + unsafe { (self.LLVMRustOffloadMapper)(v1, v2, vs.as_ptr()) } + } + + fn call_dynamic( + sysroot: &rustc_session::config::Sysroot, + ) -> Result { + let rust_offload_path = Self::get_rust_offload_path(sysroot)?; + let lib = unsafe { libloading::Library::new(rust_offload_path)? }; + + let llvm_rust_bundle_images = + *unsafe { lib.get::(b"LLVMRustBundleImages\0")? }; + let llvm_rust_offload_embed_buffer_in_module = *unsafe { + lib.get::( + b"LLVMRustOffloadEmbedBufferInModule\0", + )? + }; + let llvm_rust_offload_wrapper = + *unsafe { lib.get::(b"LLVMRustOffloadMapper\0")? }; + + Ok(Self { + LLVMRustBundleImages: llvm_rust_bundle_images, + LLVMRustOffloadEmbedBufferInModule: llvm_rust_offload_embed_buffer_in_module, + LLVMRustOffloadMapper: llvm_rust_offload_wrapper, + _lib: lib, + }) + } + + fn get_rust_offload_path( + sysroot: &rustc_session::config::Sysroot, + ) -> Result { + let llvm_version_major = unsafe { LLVMRustVersionMajor() }; + + let path_buf = sysroot + .all_paths() + .find_map(|p| { + let candidate = filesearch::make_target_lib_path(p, host_tuple()) + .join(format!("libRustOffload-{}", llvm_version_major)) + .with_extension(std::env::consts::DLL_EXTENSION); + + candidate.exists().then_some(candidate) + }) + .ok_or_else(|| { + let candidates = sysroot + .all_paths() + .map(|p| p.join("lib").display().to_string()) + .collect::>() + .join("\n* "); + RustOffloadLibraryError::NotFound { + err: format!( + "failed to find a `libRustOffload-{llvm_version_major}` \ + in the sysroot candidates:\n* {candidates}" + ), + } + })?; + + Ok(path_buf + .to_str() + .ok_or_else(|| RustOffloadLibraryError::LoadFailed { + err: format!("invalid UTF-8 in path: {}", path_buf.display()), + })? + .to_string()) + } +} diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 6892e616e1f11..b9cde145ce514 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -7,7 +7,7 @@ use std::{ptr, slice, str}; use libc::c_int; use rustc_codegen_ssa::base::wants_wasm_eh; -use rustc_codegen_ssa::target_features::cfg_target_feature; +use rustc_codegen_ssa::target_features::internal_target_features; use rustc_codegen_ssa::{TargetConfig, target_features}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::small_c_str::SmallCStr; @@ -252,6 +252,12 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option None, s => Some(LLVMFeature::new(s)), }, + Arch::Nvptx64 => match s { + "sm_101" if major >= 24 => Some(LLVMFeature::new("sm_110")), + "sm_101a" if major >= 24 => Some(LLVMFeature::new("sm_110a")), + "sm_101f" if major >= 24 => Some(LLVMFeature::new("sm_110f")), + s => Some(LLVMFeature::new(s)), + }, // Filter out features that are not supported by the current LLVM version Arch::PowerPC | Arch::PowerPC64 => match s { "power8-crypto" => Some(LLVMFeature::new("crypto")), @@ -314,7 +320,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option TargetConfig { let target_machine = create_informational_target_machine(sess, true); - let (unstable_target_features, target_features) = cfg_target_feature( + let internal_target_features = internal_target_features( sess, |feature| { to_llvm_features(sess, feature) @@ -322,9 +328,9 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { .unwrap_or_default() }, |feature| { - // This closure determines whether the target CPU has the feature according to LLVM. We do - // *not* consider the `-Ctarget-feature`s here, as that will be handled later in - // `cfg_target_feature`. + // This closure determines whether the target CPU has the feature according to LLVM. We + // do *not* consider the `-Ctarget-feature`s here, as that will be handled later in + // `internal_target_features`. if let Some(feat) = to_llvm_features(sess, feature) { // All the LLVM features this expands to must be enabled. for llvm_feature in feat { @@ -344,8 +350,7 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { ); let mut cfg = TargetConfig { - target_features, - unstable_target_features, + internal_target_features, has_reliable_f16: true, has_reliable_f16_math: true, has_reliable_f128: true, @@ -736,7 +741,10 @@ pub(crate) fn global_llvm_features(sess: &Session, for_cfg: bool) -> Vec target_features::flag_to_backend_features(sess, extend_backend_features); } - // We add this in the "base target" so that these show up in `sess.unstable_target_features`. + // `-C` flags that map to LLVM target features. + // We need to include them even with `only_base_features` as this is used to populate + // `sess.internal_target_features` where we very much want them to be present (e.g. the inline + // asm logic uses that to check which registers may be used). llvm_features_by_flags(sess, &mut features); // `-Zllvm-target-features`, all the way at the end to overwrite everything. diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 47462ca2cac91..e3e4a9bd4a712 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1073,27 +1073,43 @@ fn report_linker_output( escape_string(output.trim().as_bytes()) } + fn has_lnk_code(line: &str) -> bool { + // link.exe diagnostics are structured as `LINK : warning LNK####:` or + // `LINK : fatal error LNK####:`. The code is always followed by a `:` + // that is the second colon in the line, so matching that structure + // instead of scanning for `LNK####` anywhere avoids false positives on + // file names. + let Some((code_colon, _)) = line.match_indices(':').nth(1) else { + return false; + }; + let Some(code) = code_colon.checked_sub(7) else { + return false; + }; + let code = &line.as_bytes()[code..code_colon]; + code.starts_with(b"LNK") && code[3..].iter().all(u8::is_ascii_digit) + } + if is_msvc_link_exe(sess) { info!("inferred MSVC link.exe"); escaped_stdout = for_each(&stdout, |line, output| { - // Hide some progress messages from link.exe that we don't care about. - // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 - // When incremental linking is enabled and an .ilk exists, but its associated .exe is - // missing, link.exe prints the path of the missing .exe followed by: + // Hide progress messages from link.exe that we don't care about. + // These include localized variants of the English messages (e.g. + // "Creating library ..."), which rustc cannot recognize by text + // without the English language pack. + // See https://github.com/rust-lang/rust/issues/159133 + // When incremental linking is enabled and an .ilk exists, but its + // associated .exe is missing, link.exe prints the path of the + // missing .exe followed by: let ilk_but_no_exe = "not found or not built by the last incremental link; performing full link"; - let trimmed = line.trim_start(); - if trimmed.starts_with("Creating library") - || trimmed.starts_with("Generating code") - || trimmed.starts_with("Finished generating code") - || trimmed.ends_with(ilk_but_no_exe) - { - linker_info += line; - linker_info += "\r\n"; - } else { + // LNK6004 is the one code-bearing line that is still informational. + if has_lnk_code(line) && !line.ends_with(ilk_but_no_exe) { *output += line; *output += "\r\n" + } else { + linker_info += line; + linker_info += "\r\n"; } }); } else if is_macos_linker(sess) { diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs index dbc0abdb50da8..f8cc07201d10f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs +++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs @@ -229,7 +229,7 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport] // It is important that the order of reservation matches the order of writing. // The object crate contains many debug asserts that fire if you get this wrong. - let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features) + let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.internal_target_features) else { sess.dcx().fatal(format!( "raw-dylib is not supported for the architecture `{}`", diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 50a3e7fb7a1d1..135faa5817516 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -137,9 +137,6 @@ pub(crate) fn get_linker<'a>( // to the linker args construction. assert!(cmd.get_args().is_empty() || sess.target.cfg_abi == CfgAbi::Uwp); match flavor { - LinkerFlavor::Unix(Cc::No) if sess.target.os == Os::L4Re => { - Box::new(L4Bender::new(cmd, sess)) as Box - } LinkerFlavor::Unix(Cc::No) if sess.target.os == Os::Aix => { Box::new(AixLinker::new(cmd, sess)) as Box } @@ -279,7 +276,6 @@ generate_arg_methods! { MsvcLinker<'_> EmLinker<'_> WasmLd<'_> - L4Bender<'_> AixLinker<'_> LlbcLinker<'_> BpfLinker<'_> @@ -1468,128 +1464,6 @@ impl<'a> WasmLd<'a> { } } -/// Linker shepherd script for L4Re (Fiasco) -struct L4Bender<'a> { - cmd: Command, - sess: &'a Session, - hinted_static: bool, -} - -impl<'a> Linker for L4Bender<'a> { - fn cmd(&mut self) -> &mut Command { - &mut self.cmd - } - - fn set_output_kind( - &mut self, - _output_kind: LinkOutputKind, - _crate_type: CrateType, - _out_filename: &Path, - ) { - } - - fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) { - self.hint_static(); - if !whole_archive { - self.link_arg(format!("-PC{name}")); - } else { - self.link_arg("--whole-archive") - .link_or_cc_arg(format!("-l{name}")) - .link_arg("--no-whole-archive"); - } - } - - fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) { - self.hint_static(); - if !whole_archive { - self.link_or_cc_arg(path); - } else { - self.link_arg("--whole-archive").link_or_cc_arg(path).link_arg("--no-whole-archive"); - } - } - - fn full_relro(&mut self) { - self.link_args(&["-z", "relro", "-z", "now"]); - } - - fn partial_relro(&mut self) { - self.link_args(&["-z", "relro"]); - } - - fn no_relro(&mut self) { - self.link_args(&["-z", "norelro"]); - } - - fn gc_sections(&mut self, keep_metadata: bool) { - if !keep_metadata { - self.link_arg("--gc-sections"); - } - } - - fn optimize(&mut self) { - // GNU-style linkers support optimization with -O. GNU ld doesn't - // need a numeric argument, but other linkers do. - if self.sess.opts.optimize == config::OptLevel::More - || self.sess.opts.optimize == config::OptLevel::Aggressive - { - self.link_arg("-O1"); - } - } - - fn pgo_gen(&mut self) {} - - fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { - match strip { - Strip::None => {} - Strip::Debuginfo => { - self.link_arg("--strip-debug"); - } - Strip::Symbols => { - self.link_arg("--strip-all"); - } - } - } - - fn no_default_libraries(&mut self) { - self.cc_arg("-nostdlib"); - } - - fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[SymbolExport]) { - // ToDo, not implemented, copy from GCC - self.sess.dcx().emit_warn(diagnostics::L4BenderExportingSymbolsUnimplemented); - } - - fn windows_subsystem(&mut self, subsystem: WindowsSubsystemKind) { - let subsystem = subsystem.as_str(); - self.link_arg(&format!("--subsystem {subsystem}")); - } - - fn reset_per_library_state(&mut self) { - self.hint_static(); // Reset to default before returning the composed command line. - } - - fn linker_plugin_lto(&mut self) {} - - fn control_flow_guard(&mut self) {} - - fn ehcont_guard(&mut self) {} - - fn no_crt_objects(&mut self) {} -} - -impl<'a> L4Bender<'a> { - fn new(cmd: Command, sess: &'a Session) -> L4Bender<'a> { - L4Bender { cmd, sess, hinted_static: false } - } - - fn hint_static(&mut self) { - if !self.hinted_static { - self.link_or_cc_arg("-static"); - self.hinted_static = true; - } - } -} - /// Linker for AIX. struct AixLinker<'a> { cmd: Command, diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 951a60426b5d5..a43bf72b6a27d 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -207,7 +207,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option Endianness::Big, }; let Some((architecture, sub_architecture)) = - sess.target.object_architecture(&sess.unstable_target_features) + sess.target.object_architecture(&sess.internal_target_features) else { return None; }; @@ -328,12 +328,12 @@ pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 { let mut e_flags: u32 = 0x0; // Check if compression is enabled - if sess.target_features.contains(&sym::zca) { + if sess.internal_target_features.contains(&sym::zca) { e_flags |= elf::EF_RISCV_RVC; } // Check if RVTSO is enabled - if sess.target_features.contains(&sym::ztso) { + if sess.internal_target_features.contains(&sym::ztso) { e_flags |= elf::EF_RISCV_TSO; } diff --git a/compiler/rustc_codegen_ssa/src/diagnostics.rs b/compiler/rustc_codegen_ssa/src/diagnostics.rs index e6aab553072f2..2b77eb2cf24fb 100644 --- a/compiler/rustc_codegen_ssa/src/diagnostics.rs +++ b/compiler/rustc_codegen_ssa/src/diagnostics.rs @@ -97,10 +97,6 @@ pub(crate) struct Ld64UnimplementedModifier; #[diag("`as-needed` modifier not supported for current linker")] pub(crate) struct LinkerUnsupportedModifier; -#[derive(Diagnostic)] -#[diag("exporting symbols not implemented yet for L4Bender")] -pub(crate) struct L4BenderExportingSymbolsUnimplemented; - #[derive(Diagnostic)] #[diag("error enumerating natvis directory: {$error}")] pub(crate) struct NoNatvisDirectory { @@ -1100,7 +1096,7 @@ pub(crate) struct TargetFeatureSafeTrait { #[derive(Diagnostic)] #[diag("target feature `{$feature}` cannot be enabled with `#[target_feature]`: {$reason}")] -pub(crate) struct ForbiddenTargetFeatureAttr<'a> { +pub(crate) struct InternalOnlyTargetFeatureAttr<'a> { #[primary_span] pub span: Span, pub feature: &'a str, @@ -1233,7 +1229,7 @@ pub(crate) struct UnstableCTargetFeature<'a> { #[derive(Diagnostic)] #[diag("target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}")] -pub(crate) struct ForbiddenCTargetFeature<'a> { +pub(crate) struct InternalOnlyCTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, pub reason: &'a str, diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 9a42debe1dd97..02ae2d50390cc 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use rustc_abi::Size; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_data_structures::unord::UnordMap; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir::CRATE_HIR_ID; use rustc_hir::attrs::{CfgEntry, NativeLibKind, WindowsSubsystemKind}; use rustc_hir::def_id::CrateNum; @@ -306,14 +306,12 @@ pub struct CrateInfo { pub exported_symbols_for_lto: Vec, } -/// Target-specific options that get set in `cfg(...)`. +/// Target-specific options that get set in `sess`/`cfg(...)`. /// /// RUSTC_SPECIFIC_FEATURES should be skipped here, those are handled outside codegen. pub struct TargetConfig { - /// Options to be set in `cfg(target_features)`. - pub target_features: Vec, - /// Options to be set in `cfg(target_features)`, but including unstable features. - pub unstable_target_features: Vec, + /// Options to be set in `sess.internal_target_features`. + pub internal_target_features: UnordSet, /// Option for `cfg(target_has_reliable_f16)`, true if `f16` basic arithmetic works. pub has_reliable_f16: bool, /// Option for `cfg(target_has_reliable_f16_math)`, true if `f16` math calls work. diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 131a345fe557d..33cc321ea6d32 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -151,7 +151,7 @@ fn prefix_and_suffix<'tcx>( let asm_binary_format = &tcx.sess.target.binary_format; let is_arm = tcx.sess.target.arch == Arch::Arm; - let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode); + let is_thumb = tcx.sess.internal_target_features.contains(&sym::thumb_mode); let function_sections = tcx.sess.opts.unstable_opts.function_sections.unwrap_or(tcx.sess.target.function_sections); diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 8f459e5a218d2..69487d2039c31 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -72,7 +72,7 @@ pub(crate) fn from_target_feature_attr( // Only allow target features whose feature gates have been enabled // and which are permitted to be toggled. if let Err(reason) = stability.toggle_allowed() { - tcx.dcx().emit_err(diagnostics::ForbiddenTargetFeatureAttr { + tcx.dcx().emit_err(diagnostics::InternalOnlyTargetFeatureAttr { span: feature_span, feature: feature_str, reason, @@ -107,7 +107,7 @@ pub(crate) fn from_target_feature_attr( diagnostics::Aarch64SoftfloatNeon, ); } else { - tcx.dcx().emit_err(diagnostics::ForbiddenTargetFeatureAttr { + tcx.dcx().emit_err(diagnostics::InternalOnlyTargetFeatureAttr { span: feature_span, feature: name.as_str(), reason: "this feature is incompatible with the target ABI", @@ -122,7 +122,17 @@ pub(crate) fn from_target_feature_attr( } else { TargetFeatureKind::Enabled }; - target_features.push(TargetFeature { name, kind }) + target_features.push(TargetFeature { name, kind }); + + if !rust_target_features + .get(name.as_str()) + .is_some_and(|s| s.toggle_allowed().is_ok()) + { + tcx.dcx().span_delayed_bug( + feature_span, + format!("internal-only feature {name} should not be toggled by `#[target_feature]`"), + ); + } } } } @@ -131,7 +141,7 @@ pub(crate) fn from_target_feature_attr( /// Computes the set of target features used in a function for the purposes of /// inline assembly. fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet { - let mut target_features = tcx.sess.unstable_target_features.clone(); + let mut target_features = tcx.sess.internal_target_features.clone(); if tcx.def_kind(did).has_codegen_attrs() { let attrs = tcx.codegen_fn_attrs(did); target_features.extend(attrs.target_features.iter().map(|feature| feature.name)); @@ -164,20 +174,22 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, } } -/// Parse the value of the target spec `features` field or `-Ctarget-feature`, also expanding -/// implied features, and call the closure for each (expanded) Rust feature. If the list contains -/// a syntactically invalid item (not starting with `+`/`-`), the error callback is invoked. +/// Parse the value of the target spec `features` field or `-Ctarget-feature`, calling the closure +/// for each entry in the list, also expanding implied features (but only for actual Rust target +/// features). If the list contains a syntactically invalid item (not starting with `+`/`-`) , the +/// error callback is invoked. fn parse_rust_feature_list<'a>( sess: &'a Session, features: &'a str, err_callback: impl Fn(&'a str), mut callback: impl FnMut( /* base_feature */ &'a str, - /* with_implied */ FxHashSet<&'a str>, + /* with_implied */ Option>, /* enable */ bool, ), ) { - // A cache for the backwards implication map. + // A cache for the forward and backwards feature maps. + let mut features_map: Option> = None; let mut inverse_implied_features: Option>> = None; for feature in features.split(',') { @@ -187,13 +199,30 @@ fn parse_rust_feature_list<'a>( continue; } - callback(base_feature, sess.target.implied_target_features(base_feature), true) + let features_map = + features_map.get_or_insert_with(|| sess.target.rust_target_features_map()); + + if !features_map.contains_key(&base_feature) { + callback(base_feature, None, true); + continue; + } + + let implied_features = sess.target.implied_target_features(base_feature, &features_map); + callback(base_feature, Some(implied_features), true) } else if let Some(base_feature) = feature.strip_prefix('-') { // Skip features that are not target features, but rustc features. if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) { continue; } + let features_map = + features_map.get_or_insert_with(|| sess.target.rust_target_features_map()); + + if !features_map.contains_key(&base_feature) { + callback(base_feature, None, false); + continue; + } + // If `f1` implies `f2`, then `!f2` implies `!f1` -- this is standard logical // contraposition. So we have to find all the reverse implications of `base_feature` and // disable them, too. @@ -210,10 +239,10 @@ fn parse_rust_feature_list<'a>( // Inverse implied target features have their own inverse implied target features, so we // traverse the map until there are no more features to add. - let mut features = FxHashSet::default(); + let mut implied_features = FxHashSet::default(); let mut new_features = vec![base_feature]; while let Some(new_feature) = new_features.pop() { - if features.insert(new_feature) { + if implied_features.insert(new_feature) { if let Some(implied_features) = inverse_implied_features.get(&new_feature) { #[allow(rustc::potential_query_instability)] new_features.extend(implied_features) @@ -221,16 +250,15 @@ fn parse_rust_feature_list<'a>( } } - callback(base_feature, features, false) + callback(base_feature, Some(implied_features), false) } else if !feature.is_empty() { err_callback(feature) } } } -/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically, -/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and -/// 2nd component of the return value, respectively). +/// Utility function for a codegen backend to compute the set of all actually enabled Rust target +/// features (which will be stored in `sess.internal_target_features`). /// /// `to_backend_features` converts a Rust feature name into a list of backend feature names; this is /// used for diagnostic purposes only. @@ -242,15 +270,15 @@ fn parse_rust_feature_list<'a>( /// to target features. /// /// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled elsewhere. -pub fn cfg_target_feature<'a, const N: usize>( +pub fn internal_target_features<'a, const N: usize>( sess: &Session, to_backend_features: impl Fn(&'a str) -> SmallVec<[&'a str; N]>, mut target_base_has_feature: impl FnMut(&str) -> bool, -) -> (Vec, Vec) { - let known_features = sess.target.rust_target_features(); +) -> UnordSet { + let features_map = sess.target.rust_target_features_map(); - // Compute which of the known target features are enabled in the 'base' target machine. We only - // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now. + // Compute which of the known target features are enabled in the 'base' target machine: for + // every Rust target feature, ask the backend if it is enabled. let mut features: UnordSet = sess .target .rust_target_features() @@ -263,10 +291,14 @@ pub fn cfg_target_feature<'a, const N: usize>( // // Iteration order is irrelevant because we're collecting into an `UnordSet`. #[allow(rustc::potential_query_instability)] - sess.target.implied_target_features(base_feature).into_iter().map(|f| Symbol::intern(f)) + sess.target + .implied_target_features(base_feature, &features_map) + .into_iter() + .map(|f| Symbol::intern(f)) }) .collect(); + // State gathered for "tied features" check. let mut enabled_disabled_features = FxHashMap::default(); // Add enabled and remove disabled features. @@ -278,37 +310,23 @@ pub fn cfg_target_feature<'a, const N: usize>( sess.dcx().emit_warn(diagnostics::UnknownCTargetFeaturePrefix { feature }); }, |base_feature, new_features, enable| { - // Iteration order is irrelevant since this only influences an `FxHashMap`. - #[allow(rustc::potential_query_instability)] - enabled_disabled_features.extend(new_features.iter().map(|&s| (s, enable))); - - // Iteration order is irrelevant since this only influences an `UnordSet`. - #[allow(rustc::potential_query_instability)] - if enable { - features.extend(new_features.into_iter().map(|f| Symbol::intern(f))); - } else { - // Remove `new_features` from `features`. - for new in new_features { - features.remove(&Symbol::intern(new)); - } - } - - // Check feature validity. - let feature_state = known_features.iter().find(|&&(v, _, _)| v == base_feature); - match feature_state { + match features_map.get(base_feature) { None => { - // This is definitely not a valid Rust feature name. Maybe it is a backend - // feature name? If so, give a better error message. - let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| { - let backend_features = to_backend_features(rust_feature); - if backend_features.contains(&base_feature) - && !backend_features.contains(&rust_feature) - { - Some(rust_feature) - } else { - None - } - }); + // This is definitely not a valid Rust feature name. We do not add it to + // `features`. Maybe it is a backend feature name? If so, give a better error + // message. + let rust_feature = sess.target.rust_target_features().iter().find_map( + |&(rust_feature, _, _)| { + let backend_features = to_backend_features(rust_feature); + if backend_features.contains(&base_feature) + && !backend_features.contains(&rust_feature) + { + Some(rust_feature) + } else { + None + } + }, + ); let unknown_feature = if let Some(rust_feature) = rust_feature { diagnostics::UnknownCTargetFeature { feature: base_feature, @@ -322,9 +340,27 @@ pub fn cfg_target_feature<'a, const N: usize>( }; sess.dcx().emit_warn(unknown_feature); } - Some((_, stability, _)) => { - if let Stability::Forbidden { reason, hard_error } = stability { - let diag = diagnostics::ForbiddenCTargetFeature { + Some((stability, _)) => { + let new_features = new_features.unwrap(); + // Add feature to our set -- only if it is actually a recognized feature. + // Iteration order is irrelevant since this only influences an `FxHashMap`. + #[allow(rustc::potential_query_instability)] + enabled_disabled_features.extend(new_features.iter().map(|&s| (s, enable))); + + // Iteration order is irrelevant since this only influences an `UnordSet`. + #[allow(rustc::potential_query_instability)] + if enable { + features.extend(new_features.into_iter().map(|f| Symbol::intern(f))); + } else { + // Remove `new_features` from `features`. + for new in new_features { + features.remove(&Symbol::intern(new)); + } + } + + // Check feature stability. + if let Stability::InternalOnly { reason, hard_error } = stability { + let diag = diagnostics::InternalOnlyCTargetFeature { feature: base_feature, enabled: if enable { "enabled" } else { "disabled" }, reason, @@ -363,34 +399,11 @@ pub fn cfg_target_feature<'a, const N: usize>( }); } - // Filter enabled features based on feature gates. - let f = |allow_unstable| { - sess.target - .rust_target_features() - .iter() - .filter_map(|(feature, gate, _)| { - // The `allow_unstable` set is used by rustc internally to determine which target - // features are truly available, so we want to return even perma-unstable - // "forbidden" features. - if allow_unstable - || (gate.in_cfg() - && (sess.is_nightly_build() - || gate.requires_nightly(/* in_cfg */ true).is_none())) - { - Some(Symbol::intern(feature)) - } else { - None - } - }) - .filter(|feature| features.contains(&feature)) - .collect() - }; - - (f(true), f(false)) + features } /// Given a map from target_features to whether they are enabled or disabled, ensure only valid -/// combinations are allowed. +/// combinations are allowed. Returns `Some` if a violation is found. pub fn check_tied_features( sess: &Session, features: &FxHashMap<&str, bool>, @@ -416,8 +429,6 @@ pub fn target_spec_to_backend_features<'a>( sess: &'a Session, mut extend_backend_features: impl FnMut(&'a str, /* enable */ bool), ) { - let mut rust_features = vec![]; - // This check handles SM versions that defaults (by LLVM) to unsupported (by Rust) PTX ISA versions. // sm_70, sm_72 and sm_75 defaults to PTX ISA versions with major version 6, while sm_80 default to 7.0 if sess.target.arch == Arch::Nvptx64 @@ -426,7 +437,7 @@ pub fn target_spec_to_backend_features<'a>( None | Some("sm_70") | Some("sm_72") | Some("sm_75") ) { - rust_features.push((true, "ptx70")); + extend_backend_features("ptx70", true); } // Compute implied features @@ -435,20 +446,18 @@ pub fn target_spec_to_backend_features<'a>( &sess.target.features, /* err_callback */ |feature| { - panic!("Target spec contains invalid feature {feature}"); + panic!("Target spec contains invalid feature {feature} (missing `+`/`-` prefix)"); }, - |_base_feature, new_features, enable| { - // FIXME emit an error for unknown features like cfg_target_feature would for -Ctarget-feature - rust_features.extend( - UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)), - ); + |base_feature, new_features, enable| { + // FIXME emit an error for unknown features in the target spec like + // internal_target_features would for -Ctarget-feature. + let new_features = + new_features.unwrap_or_else(|| FxHashSet::from_iter(std::iter::once(base_feature))); + for new_feature in UnordSet::from(new_features).to_sorted_stable_ord().iter() { + extend_backend_features(new_feature, enable); + } }, ); - - // Add this to the backend features. - for (enable, feature) in rust_features { - extend_backend_features(feature, enable); - } } /// Translates the `-Ctarget-feature` flag into a backend target feature list. @@ -459,26 +468,22 @@ pub fn flag_to_backend_features<'a>( sess: &'a Session, mut extend_backend_features: impl FnMut(&'a str, /* enable */ bool), ) { - // Compute implied features - let mut rust_features = vec![]; parse_rust_feature_list( sess, &sess.opts.cg.target_feature, /* err_callback */ |_feature| { - // Errors are already emitted in `cfg_target_feature`; avoid duplicates. + // Errors are already emitted in `internal_target_features`; avoid duplicates. }, - |_base_feature, new_features, enable| { - rust_features.extend( - UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)), - ); + |base_feature, new_features, enable| { + // Forward unknown features to the backend as that's what we have always done. + let new_features = + new_features.unwrap_or_else(|| FxHashSet::from_iter(std::iter::once(base_feature))); + for new_feature in UnordSet::from(new_features).to_sorted_stable_ord().iter() { + extend_backend_features(new_feature, enable); + } }, ); - - // Add this to the backend features. - for (enable, feature) in rust_features { - extend_backend_features(feature, enable); - } } /// Computes the backend target features to be added to account for retpoline flags. @@ -533,9 +538,12 @@ pub(crate) fn provide(providers: &mut Providers) { (Stability::Stable, _) | ( Stability::Unstable { .. }, - Stability::Unstable { .. } | Stability::Forbidden { .. }, + Stability::Unstable { .. } | Stability::InternalOnly { .. }, ) - | (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => { + | ( + Stability::InternalOnly { .. }, + Stability::InternalOnly { .. }, + ) => { // The stability in the entry is at least as good as the new // one, just keep it. } @@ -553,13 +561,18 @@ pub(crate) fn provide(providers: &mut Providers) { .target .rust_target_features() .iter() - .map(|(a, b, _)| (a.to_string(), *b)) + .map(|(feat, stab, _)| (feat.to_string(), *stab)) .collect() } }, implied_target_features: |tcx, feature: Symbol| { + if tcx.sess.opts.actually_rustdoc { + // We can't handle implication when we are mixing all targets. + return vec![feature]; + } + let features_map = tcx.sess.target.rust_target_features_map(); let feature = feature.as_str(); - UnordSet::from(tcx.sess.target.implied_target_features(feature)) + UnordSet::from(tcx.sess.target.implied_target_features(feature, &features_map)) .into_sorted_stable_ord() .into_iter() .map(|s| Symbol::intern(s)) diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 85882af9e7cd0..36f4d858d0be5 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -44,8 +44,7 @@ pub trait CodegenBackend { /// `target_feature` and support for unstable float types. fn target_config(&self, _sess: &Session) -> TargetConfig { TargetConfig { - target_features: vec![], - unstable_target_features: vec![], + internal_target_features: Default::default(), // `true` is used as a default so backends need to acknowledge when they do not // support the float types, rather than accidentally quietly skipping all tests. has_reliable_f16: true, diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index c3c9f26c31571..7e7f72943c7cd 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -42,7 +42,6 @@ pub struct AnnotateSnippetEmitter { ui_testing: bool, ignored_directories_in_source_blocks: Vec, diagnostic_width: Option, - macro_backtrace: bool, track_diagnostics: bool, terminal_url: TerminalUrl, @@ -51,18 +50,30 @@ pub struct AnnotateSnippetEmitter { impl Debug for AnnotateSnippetEmitter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let AnnotateSnippetEmitter { + dst, + sm, + short_message, + ui_testing, + ignored_directories_in_source_blocks, + diagnostic_width, + macro_backtrace, + track_diagnostics, + terminal_url, + theme, + } = self; + f.debug_struct("AnnotateSnippetEmitter") - .field("short_message", &self.short_message) - .field("ui_testing", &self.ui_testing) - .field( - "ignored_directories_in_source_blocks", - &self.ignored_directories_in_source_blocks, - ) - .field("diagnostic_width", &self.diagnostic_width) - .field("macro_backtrace", &self.macro_backtrace) - .field("track_diagnostics", &self.track_diagnostics) - .field("terminal_url", &self.terminal_url) - .field("theme", &self.theme) + .field("dst", &format_args!("")) + .field("sm", sm) + .field("short_message", short_message) + .field("ui_testing", ui_testing) + .field("ignored_directories_in_source_blocks", ignored_directories_in_source_blocks) + .field("diagnostic_width", diagnostic_width) + .field("macro_backtrace", macro_backtrace) + .field("track_diagnostics", track_diagnostics) + .field("terminal_url", terminal_url) + .field("theme", theme) .finish() } } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 045233c0c4d21..c80ffe625f248 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -858,6 +858,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)), } } else if let SyntaxExtensionKind::LegacyAttr(expander) = ext { + self.gate_proc_macro_attr_item(span, &item); // `LegacyAttr` is only used for builtin attribute macros, which have their // safety checked by `check_builtin_meta_item`, so we don't need to check // `unsafety` here. @@ -1045,7 +1046,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.sess, sym::proc_macro_hygiene, span, - format!("custom attributes cannot be applied to {kind}"), + format!("macro attributes on {kind} are unstable"), ) .emit(); } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 00a2fc299efd8..9d8b0e101d374 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -457,6 +457,7 @@ language_item_table! { // Used to fallback `{float}` to `f32` when `f32: From<{float}>` From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1); + FromFn, sym::from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index d33a1cf736738..ab34246d9716e 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -2,8 +2,6 @@ //! //! For more information about delegation design, see the tracking issue #118212. -use std::debug_assert_matches; - use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -104,14 +102,17 @@ enum FnKind { fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: impl Into) -> FnKind { let def_id = def_id.into(); - debug_assert_matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn); - - let parent = tcx.parent(def_id); - match tcx.def_kind(parent) { - DefKind::Trait => FnKind::AssocTrait, - DefKind::Impl { of_trait: true } => FnKind::AssocTraitImpl, - DefKind::Impl { of_trait: false } => FnKind::AssocInherentImpl, - _ => FnKind::Free, + match tcx.def_kind(def_id) { + DefKind::Fn => FnKind::Free, + DefKind::AssocFn => match tcx.def_kind(tcx.parent(def_id)) { + DefKind::Trait => FnKind::AssocTrait, + DefKind::Impl { of_trait } => match of_trait { + true => FnKind::AssocTraitImpl, + false => FnKind::AssocInherentImpl, + }, + _ => unreachable!("associated function can only be in trait or impl"), + }, + _ => unreachable!("delegation/signature can be either free or associated function"), } } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 3074a5900773d..d2430a06a0072 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -31,6 +31,18 @@ use crate::method::TreatNotYetDefinedOpaques; use crate::method::confirm::ConfirmContext; use crate::method::probe::{IsSuggestion, Mode}; +/// Side-table info for lowering splatted function arguments. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(crate) enum SplatLoweringInfo<'tcx> { + /// The DefId of the FnDef being called, used to look up the function type. + /// Also used during argument suggestion for non-splatted function calls. + FnDef(DefId), + /// The type of the FnPtr being called. + FnPtr(Ty<'tcx>), + /// Type resolution errored. + Error(ErrorGuaranteed), +} + /// Checks that it is legal to call methods of the trait corresponding /// to `trait_id` (this only cares about the trait, not the specific /// method that is called). @@ -600,13 +612,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let fn_sig = self.normalize(call_expr.span, Unnormalized::new_wip(fn_sig)); + // Splatted FnDefs use the DefId to look up the type, FnPtrs need it directly + let fn_id = match def_id { + Some(x) => SplatLoweringInfo::FnDef(x), + None => SplatLoweringInfo::FnPtr(callee_ty), + }; + self.check_argument_types_maybe_method_like( &fn_sig, call_expr, arg_exprs, expected, TupleArgumentsFlag::with_fn_sig_kind(fn_sig.fn_sig_kind, false), - def_id, + fn_id, callee_generic_args, ); @@ -643,7 +661,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, tuple_arguments_flag: TupleArgumentsFlag, - def_id: Option, + fn_id: SplatLoweringInfo<'tcx>, callee_generic_args: Option>, ) { let do_check = || { @@ -656,7 +674,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs, fn_sig.c_variadic(), tuple_arguments_flag, - def_id, + fn_id, callee_generic_args, ); }; @@ -1074,7 +1092,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs, fn_sig.fn_sig_kind.c_variadic(), TupleArgumentsFlag::rust_fn_trait_call(), - Some(closure_def_id.to_def_id()), + SplatLoweringInfo::FnDef(closure_def_id.to_def_id()), None, ); @@ -1172,7 +1190,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs, method.sig.fn_sig_kind.c_variadic(), TupleArgumentsFlag::rust_fn_trait_call(), - Some(method.def_id), + SplatLoweringInfo::FnDef(method.def_id), None, ); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index e2aef3c0bdb8c..bfdd99b6277ba 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -40,6 +40,7 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt}; use tracing::{debug, instrument, trace}; use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; +use crate::callee::SplatLoweringInfo; use crate::coercion::CoerceMany; use crate::diagnostics::{ AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, @@ -1489,7 +1490,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args, method.sig.fn_sig_kind.c_variadic(), method_tuple_args_flag, - Some(method.def_id), + SplatLoweringInfo::FnDef(method.def_id), Some(method.args), ); @@ -1501,22 +1502,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let guar = self.report_method_error(expr.hir_id, rcvr_t, error, expected, false); let err_inputs = self.err_args(args.len(), guar); - let err_output = Ty::new_error(self.tcx, guar); + let err_ty = Ty::new_error(self.tcx, guar); self.check_argument_types( segment.ident.span, expr, &err_inputs, - err_output, + err_ty, NoExpectation, args, false, TupleArgumentsFlag::DontTupleArguments, - None, + SplatLoweringInfo::Error(guar), Some(GenericArgsRef::default()), ); - err_output + err_ty } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 5b9caed6af9c9..5faa3dfa494cb 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -43,7 +43,7 @@ use rustc_trait_selection::traits::{ }; use tracing::{debug, instrument}; -use crate::callee::{self, DeferredCallResolution}; +use crate::callee::{self, DeferredCallResolution, SplatLoweringInfo}; use crate::diagnostics::{self, CtorIsPrivate}; use crate::method::{self, MethodCallee}; use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy}; @@ -240,7 +240,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn write_splatted_resolution( &self, hir_id: HirId, - r: Result, + r: Result, ErrorGuaranteed>, ) { self.typeck_results.borrow_mut().splatted_defs_mut().insert(hir_id, r); } @@ -262,7 +262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, hir_id: HirId, span: Span, - callee_def_id: Option, + fn_id: SplatLoweringInfo<'tcx>, callee_generic_args: Option>, first_tupled_arg_index: u16, tupled_args_count: u16, @@ -270,16 +270,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME(const_trait_impl): enforce constness using enforce_context_effects() and add // _and_enforce_effects to this method's name - self.write_splatted_resolution( - hir_id, - Ok(SplattedDef { - def_id: callee_def_id, - arg_index: first_tupled_arg_index, - arg_count: tupled_args_count, - }), - ); - if let Some(callee_generic_args) = callee_generic_args { - self.write_args(hir_id, callee_generic_args); + match fn_id { + // We're splatting a FnDef based on its DefId + SplatLoweringInfo::FnDef(def_id) => { + self.write_splatted_resolution( + hir_id, + Ok(SplattedDef::FnDef { + def_id, + arg_index: first_tupled_arg_index, + arg_count: tupled_args_count, + }), + ); + if let Some(callee_generic_args) = callee_generic_args { + self.write_args(hir_id, callee_generic_args); + } + } + // We're splatting a FnPtr based on its type + SplatLoweringInfo::FnPtr(fn_ty) => { + // FIXME(splat): do we need to look up both these HirIds? + // They can be different (and are different in some UI tests) + self.write_splatted_resolution( + hir_id, + Ok(SplattedDef::FnPtr { + fn_ptr_type: fn_ty, + arg_index: first_tupled_arg_index, + arg_count: tupled_args_count, + }), + ); + // FIXME(splat): is this actually populated and used correctly? + if let Some(callee_generic_args) = callee_generic_args { + self.write_args(hir_id, callee_generic_args); + } + } + SplatLoweringInfo::Error(guar) => { + self.write_splatted_resolution(hir_id, Err(guar)); + if let Some(callee_generic_args) = callee_generic_args { + self.write_args(hir_id, callee_generic_args); + } + } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index f1c98c16651cc..b9262e40f6f31 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -33,6 +33,7 @@ use tracing::debug; use crate::Expectation::*; use crate::TupleArgumentsFlag::*; +use crate::callee::SplatLoweringInfo; use crate::coercion::CoerceMany; use crate::diagnostics::SuggestPtrNullMut; use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx}; @@ -204,8 +205,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { c_variadic: bool, // Whether all the arguments have been bundled in a tuple (ex: closures), or one has been splatted tuple_arguments: TupleArgumentsFlag, - // The DefId for the function being called, for better error messages - fn_def_id: Option, + // Lowering info if a splatted function is being called. + fn_id: SplatLoweringInfo<'tcx>, // The generics of the function being called. Only used for splatting callee_generic_args: Option>, ) { @@ -302,7 +303,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { provided_args, expected_input_tys, tuple_arguments, - fn_def_id, + fn_id, callee_generic_args, ); let TupledArgCheckOutcome { @@ -553,7 +554,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { provided_args, c_variadic, err_code, - fn_def_id, + fn_id, call_span, call_expr, tuple_arguments, @@ -576,8 +577,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mut expected_input_tys: Option>>, // Whether all the arguments have been bundled in a tuple (ex: closures), or one has been splatted tuple_arguments: TupleArgumentsFlag, - // The DefId for the function being called, for better error messages - fn_def_id: Option, + // Lowering info if a splatted function is being called. + fn_id: SplatLoweringInfo<'tcx>, // The generics of the function being called. Only used for splatting callee_generic_args: Option>, ) -> TupledArgCheckOutcome<'tcx> { @@ -737,7 +738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we don't check argument counts here, and there's a subtle bug in the code above, // later compilation stages can fail in unrelated places with confusing errors. if !matches!(tuple_type.kind(), ty::Tuple(_)) { - let spans = if let Some(def_id) = fn_def_id + let spans = if let SplatLoweringInfo::FnDef(def_id) = fn_id && let Some(hir_node) = self.tcx.hir_get_if_local(def_id) && let Some(fn_decl) = hir_node.fn_decl() && let Some(arg_ty) = fn_decl.inputs.get(first_tupled_arg_index_usz) @@ -798,7 +799,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_splatted_call( call_expr.hir_id, call_span, - fn_def_id, + fn_id, callee_generic_args, first_tupled_arg_index, tupled_args_count.unwrap().try_into().unwrap(), @@ -835,7 +836,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { provided_args: IndexVec>, c_variadic: bool, err_code: ErrCode, - fn_def_id: Option, + // Lowering info if a splatted function is being called. + fn_id: SplatLoweringInfo<'tcx>, call_span: Span, call_expr: &'tcx hir::Expr<'tcx>, // FIXME(splat): when the feature design is settled, improve the errors here @@ -850,7 +852,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { provided_args, c_variadic, err_code, - fn_def_id, + fn_id, call_span, call_expr, tuple_arguments, @@ -924,7 +926,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Call out where the function is defined fn_call_diag_ctxt.label_fn_like( &mut err, - fn_def_id, + fn_id, fn_call_diag_ctxt.callee_ty, call_expr, None, @@ -1594,7 +1596,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn label_fn_like( &self, err: &mut Diag<'_>, - callable_def_id: Option, + // Lowering info if a splatted function is being called. + callable_id: SplatLoweringInfo<'tcx>, callee_ty: Option>, call_expr: &'tcx hir::Expr<'tcx>, expected_ty: Option>, @@ -1605,7 +1608,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_method: bool, tuple_arguments: TupleArgumentsFlag, ) { - let Some(mut def_id) = callable_def_id else { + let SplatLoweringInfo::FnDef(mut def_id) = callable_id else { + // FIXME(FnPtr, splat): Handle FnPtr types and splatting here return; }; @@ -1944,14 +1948,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn label_generic_mismatches( &self, err: &mut Diag<'_>, - callable_def_id: Option, + // Lowering info if a splatted function is being called. + callable_id: SplatLoweringInfo<'tcx>, matched_inputs: &IndexVec>, provided_arg_tys: &IndexVec, Span)>, formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, is_method: bool, is_splat: bool, ) { - let Some(def_id) = callable_def_id else { + let SplatLoweringInfo::FnDef(def_id) = callable_id else { + // FIXME(FnPtr, splat): Handle FnPtr types and splatting here return; }; @@ -2188,7 +2194,8 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { provided_args: IndexVec>, c_variadic: bool, err_code: ErrCode, - fn_def_id: Option, + // Lowering info if a splatted function is being called. + fn_id: SplatLoweringInfo<'tcx>, call_span: Span, call_expr: &'tcx Expr<'tcx>, tuple_arguments: TupleArgumentsFlag, @@ -2200,7 +2207,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { provided_args, c_variadic, err_code, - fn_def_id, + fn_id, call_span, call_expr, tuple_arguments, @@ -2311,7 +2318,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { }; self.arg_matching_ctxt.args_ctxt.call_ctxt.fn_ctxt.label_fn_like( &mut err, - self.fn_def_id, + self.fn_id, self.callee_ty, self.call_expr, None, @@ -2469,7 +2476,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { // Call out where the function is defined self.label_fn_like( &mut err, - self.fn_def_id, + self.fn_id, self.callee_ty, self.call_expr, Some(expected_ty), @@ -2888,7 +2895,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { fn label_generic_mismatches(&self, err: &mut Diag<'a>) { self.fn_ctxt.label_generic_mismatches( err, - self.fn_def_id, + self.fn_id, &self.matched_inputs, &self.provided_arg_tys, &self.formal_and_expected_inputs, @@ -3083,7 +3090,8 @@ impl<'a, 'tcx> ArgMatchingCtxt<'a, 'tcx> { provided_args: IndexVec>, c_variadic: bool, err_code: ErrCode, - fn_def_id: Option, + // Lowering info if a splatted function is being called. + fn_id: SplatLoweringInfo<'tcx>, call_span: Span, call_expr: &'tcx Expr<'tcx>, tuple_arguments: TupleArgumentsFlag, @@ -3095,7 +3103,7 @@ impl<'a, 'tcx> ArgMatchingCtxt<'a, 'tcx> { provided_args, c_variadic, err_code, - fn_def_id, + fn_id, call_span, call_expr, tuple_arguments, @@ -3230,7 +3238,8 @@ impl<'a, 'tcx> ArgsCtxt<'a, 'tcx> { provided_args: IndexVec>, c_variadic: bool, err_code: ErrCode, - fn_def_id: Option, + // Lowering info if a splatted function is being called. + fn_id: SplatLoweringInfo<'tcx>, call_span: Span, call_expr: &'tcx Expr<'tcx>, tuple_arguments: TupleArgumentsFlag, @@ -3242,7 +3251,7 @@ impl<'a, 'tcx> ArgsCtxt<'a, 'tcx> { provided_args, c_variadic, err_code, - fn_def_id, + fn_id, call_span, call_expr, tuple_arguments, @@ -3349,7 +3358,8 @@ struct CallCtxt<'a, 'tcx> { provided_args: IndexVec>, c_variadic: bool, err_code: ErrCode, - fn_def_id: Option, + /// Lowering info if a splatted function is being called. + fn_id: SplatLoweringInfo<'tcx>, call_span: Span, call_expr: &'tcx hir::Expr<'tcx>, tuple_arguments: TupleArgumentsFlag, @@ -3373,7 +3383,8 @@ impl<'a, 'tcx> CallCtxt<'a, 'tcx> { provided_args: IndexVec>, c_variadic: bool, err_code: ErrCode, - fn_def_id: Option, + // Lowering info if a splatted function is being called. + fn_id: SplatLoweringInfo<'tcx>, call_span: Span, call_expr: &'tcx hir::Expr<'tcx>, tuple_arguments: TupleArgumentsFlag, @@ -3405,7 +3416,7 @@ impl<'a, 'tcx> CallCtxt<'a, 'tcx> { provided_args, c_variadic, err_code, - fn_def_id, + fn_id, call_span, call_expr, tuple_arguments, @@ -3492,7 +3503,7 @@ impl<'a, 'tcx> CallCtxt<'a, 'tcx> { "()".to_string() } else if ty.is_suggestable(self.tcx, false) { with_forced_trimmed_paths!(format!("/* {ty} */")) - } else if let Some(fn_def_id) = self.fn_def_id + } else if let SplatLoweringInfo::FnDef(fn_def_id) = self.fn_id && self.tcx.def_kind(fn_def_id).is_fn_like() && let self_implicit = matches!(self.call_expr.kind, hir::ExprKind::MethodCall(..)) as usize @@ -3502,6 +3513,9 @@ impl<'a, 'tcx> CallCtxt<'a, 'tcx> { { format!("/* {} */", arg.name) } else { + // FIXME(FnPtr, splat): What suggestions are needed for FnPtrs? + // SplatLoweringInfo::FnPtr(Ty) and SplatLoweringInfo::Error currently fall through to + // this placeholder "/* value */".to_string() } } diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 3ce52256d618b..654a782262e22 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -11,7 +11,7 @@ use rustc_ast as ast; use rustc_attr_parsing::ShouldEmit; use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder}; use rustc_codegen_ssa::back::link::link_binary; -use rustc_codegen_ssa::target_features::cfg_target_feature; +use rustc_codegen_ssa::target_features::internal_target_features; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig}; use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; @@ -50,10 +50,27 @@ pub(crate) fn add_configuration( let tf = sym::target_feature; let tf_cfg = codegen_backend.target_config(sess); - sess.unstable_target_features.extend(tf_cfg.unstable_target_features.iter().copied()); - sess.target_features.extend(tf_cfg.target_features.iter().copied()); + // Add some of the target features to `cfg`. + cfg.extend( + sess.target + .rust_target_features() + .iter() + .filter_map(|(feature, gate, _)| { + if gate.in_cfg() + && (sess.is_nightly_build() + || gate.requires_nightly(/* in_cfg */ true).is_none()) + { + Some(Symbol::intern(feature)) + } else { + None + } + }) + .filter(|feature| tf_cfg.internal_target_features.contains(&feature)) + .map(|feature| (sym::target_feature, Some(feature))), + ); - cfg.extend(tf_cfg.target_features.into_iter().map(|feat| (tf, Some(feat)))); + // Store all of them in the session. + sess.internal_target_features.extend(tf_cfg.internal_target_features.into_sorted_stable_ord()); if tf_cfg.has_reliable_f16 { cfg.insert((sym::target_has_reliable_f16, None)); @@ -74,10 +91,10 @@ pub(crate) fn add_configuration( } /// Ensures that all target features required by the ABI are present. -/// Must be called after `unstable_target_features` has been populated! +/// Must be called after `internal_target_features` has been populated! pub(crate) fn check_abi_required_features(sess: &Session) { let abi_feature_constraints = sess.target.abi_required_features(); - // We check this against `unstable_target_features` as that is conveniently already + // We check this against `internal_target_features` as that is conveniently already // back-translated to rustc feature names, taking into account `-Ctarget-cpu` and `-Ctarget-feature`. // Just double-check that the features we care about are actually on our list. for feature in @@ -90,13 +107,13 @@ pub(crate) fn check_abi_required_features(sess: &Session) { } for feature in abi_feature_constraints.required { - if !sess.unstable_target_features.contains(&Symbol::intern(feature)) { + if !sess.internal_target_features.contains(&Symbol::intern(feature)) { sess.dcx() .emit_warn(diagnostics::AbiRequiredTargetFeature { feature, enabled: "enabled" }); } } for feature in abi_feature_constraints.incompatible { - if sess.unstable_target_features.contains(&Symbol::intern(feature)) { + if sess.internal_target_features.contains(&Symbol::intern(feature)) { sess.dcx() .emit_warn(diagnostics::AbiRequiredTargetFeature { feature, enabled: "disabled" }); } @@ -374,7 +391,7 @@ impl CodegenBackend for DummyCodegenBackend { } let abi_required_features = sess.target.abi_required_features(); - let (target_features, unstable_target_features) = cfg_target_feature::<0>( + let internal_target_features = internal_target_features::<0>( sess, |_feature| Default::default(), |feature| { @@ -387,8 +404,7 @@ impl CodegenBackend for DummyCodegenBackend { ); TargetConfig { - target_features, - unstable_target_features, + internal_target_features, has_reliable_f16: true, has_reliable_f16_math: true, has_reliable_f128: true, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index f500041a12d8b..983a506bd4ac6 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -164,111 +164,6 @@ extern "C" bool LLVMRustIsCall(LLVMValueRef V) { return llvm::isa(llvm::unwrap(V)); } -// Some of the functions here rely on LLVM modules that may not always be -// available. As such, we only try to build it in the first place, if -// llvm.offload is enabled. -#ifdef OFFLOAD -static Error writeFile(StringRef Filename, StringRef Data) { - Expected> OutputOrErr = - FileOutputBuffer::create(Filename, Data.size()); - if (!OutputOrErr) - return OutputOrErr.takeError(); - std::unique_ptr Output = std::move(*OutputOrErr); - llvm::copy(Data, Output->getBufferStart()); - if (Error E = Output->commit()) - return E; - return Error::success(); -} - -// This is the first of many steps in creating a binary using llvm offload, -// to run code on the gpu. Concrete, it replaces the following binary use: -// clang-offload-packager -o device.bin -// --image=file=device.bc,triple=amdgcn-amd-amdhsa,arch=gfx90a,kind=openmp -// The input module is the rust code compiled for a gpu target like amdgpu. -// Based on clang/tools/clang-offload-packager/ClangOffloadPackager.cpp -extern "C" bool LLVMRustBundleImages(LLVMModuleRef M, TargetMachine &TM, - const char *HostOutPath) { - std::string Storage; - llvm::raw_string_ostream OS1(Storage); - llvm::WriteBitcodeToFile(*unwrap(M), OS1); - OS1.flush(); - auto MB = llvm::MemoryBuffer::getMemBufferCopy(Storage, "device.bc"); - - SmallVector BinaryData; - raw_svector_ostream OS2(BinaryData); - - OffloadBinary::OffloadingImage ImageBinary{}; - ImageBinary.TheImageKind = object::IMG_Bitcode; - ImageBinary.Image = std::move(MB); - ImageBinary.TheOffloadKind = object::OFK_OpenMP; - - std::string TripleStr = TM.getTargetTriple().str(); - llvm::StringRef CPURef = TM.getTargetCPU(); - ImageBinary.StringData["triple"] = TripleStr; - ImageBinary.StringData["arch"] = CPURef; - llvm::SmallString<0> Buffer = OffloadBinary::write(ImageBinary); - if (Buffer.size() % OffloadBinary::getAlignment() != 0) - // Offload binary has invalid size alignment - return false; - OS2 << Buffer; - if (Error E = writeFile(HostOutPath, - StringRef(BinaryData.begin(), BinaryData.size()))) - return false; - return true; -} - -extern "C" bool LLVMRustOffloadEmbedBufferInModule(LLVMModuleRef HostM, - const char *HostOutPath) { - auto MBOrErr = MemoryBuffer::getFile(HostOutPath); - if (!MBOrErr) { - auto E = MBOrErr.getError(); - auto _B = errorCodeToError(E); - return false; - } - MemoryBufferRef Buf = (*MBOrErr)->getMemBufferRef(); - Module *M = unwrap(HostM); - StringRef SectionName = ".llvm.offloading"; - Align Alignment = Align(8); - llvm::embedBufferInModule(*M, Buf, SectionName, Alignment); - return true; -} - -// Clone OldFn into NewFn, remapping its arguments to RebuiltArgs. -// Each arg of OldFn is replaced with the corresponding value in RebuiltArgs. -// For scalars, RebuiltArgs contains the value cast and/or truncated to the -// original type. -extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn, - const LLVMValueRef *RebuiltArgs) { - llvm::Function *oldFn = llvm::unwrap(OldFn); - llvm::Function *newFn = llvm::unwrap(NewFn); - - // Map old arguments to new arguments. We skip the first dyn_ptr argument, - // since it can't be used directly by user code. - llvm::ValueToValueMapTy vmap; - auto newArgIt = newFn->arg_begin(); - newArgIt->setName("dyn_ptr"); - - unsigned i = 0; - for (auto &oldArg : oldFn->args()) { - vmap[&oldArg] = unwrap(RebuiltArgs[i++]); - } - - llvm::SmallVector returns; - llvm::CloneFunctionInto(newFn, oldFn, vmap, - llvm::CloneFunctionChangeType::LocalChangesOnly, - returns); - - BasicBlock &entry = newFn->getEntryBlock(); - BasicBlock &clonedEntry = *std::next(newFn->begin()); - - if (entry.getTerminator()) - entry.getTerminator()->eraseFromParent(); - - IRBuilder<> B(&entry); - B.CreateBr(&clonedEntry); -} -#endif - extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name, size_t NameLen) { return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen))); diff --git a/compiler/rustc_llvm/llvm-wrapper/offload/CMakeLists.txt b/compiler/rustc_llvm/llvm-wrapper/offload/CMakeLists.txt new file mode 100644 index 0000000000000..37c747a902d87 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/offload/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.20) +project(RustOffload LANGUAGES CXX) + +find_package(LLVM CONFIG REQUIRED) + +add_library(RustOffload-${LLVM_VERSION_MAJOR} SHARED + OffloadWrapper.cpp +) + +target_include_directories(RustOffload-${LLVM_VERSION_MAJOR} PRIVATE + ${LLVM_INCLUDE_DIRS} +) + +target_link_libraries(RustOffload-${LLVM_VERSION_MAJOR} PRIVATE + LLVM +) + +if(NOT LLVM_ENABLE_RTTI) + target_compile_options( + RustOffload-${LLVM_VERSION_MAJOR} + PRIVATE -fno-rtti + ) +endif() + +install(TARGETS RustOffload-${LLVM_VERSION_MAJOR} + LIBRARY DESTINATION lib +) diff --git a/compiler/rustc_llvm/llvm-wrapper/offload/OffloadWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/offload/OffloadWrapper.cpp new file mode 100644 index 0000000000000..8c18f2453e9d8 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/offload/OffloadWrapper.cpp @@ -0,0 +1,117 @@ +#include "../SuppressLLVMWarnings.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/Object/OffloadBinary.h" +#include "llvm/Support/CBindingWrapping.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" +#include "llvm/Transforms/Utils/ValueMapper.h" + +using namespace llvm; +using namespace llvm::object; + +static Error writeFile(StringRef Filename, StringRef Data) { + Expected> OutputOrErr = + FileOutputBuffer::create(Filename, Data.size()); + if (!OutputOrErr) + return OutputOrErr.takeError(); + std::unique_ptr Output = std::move(*OutputOrErr); + llvm::copy(Data, Output->getBufferStart()); + if (Error E = Output->commit()) + return E; + return Error::success(); +} + +// This is the first of many steps in creating a binary using llvm offload, +// to run code on the gpu. Concrete, it replaces the following binary use: +// clang-offload-packager -o device.bin +// --image=file=device.bc,triple=amdgcn-amd-amdhsa,arch=gfx90a,kind=openmp +// The input module is the rust code compiled for a gpu target like amdgpu. +// Based on clang/tools/clang-offload-packager/ClangOffloadPackager.cpp +extern "C" bool LLVMRustBundleImages(LLVMModuleRef M, TargetMachine &TM, + const char *HostOutPath) { + std::string Storage; + llvm::raw_string_ostream OS1(Storage); + llvm::WriteBitcodeToFile(*unwrap(M), OS1); + OS1.flush(); + auto MB = llvm::MemoryBuffer::getMemBufferCopy(Storage, "device.bc"); + + SmallVector BinaryData; + raw_svector_ostream OS2(BinaryData); + + OffloadBinary::OffloadingImage ImageBinary{}; + ImageBinary.TheImageKind = object::IMG_Bitcode; + ImageBinary.Image = std::move(MB); + ImageBinary.TheOffloadKind = object::OFK_OpenMP; + + std::string TripleStr = TM.getTargetTriple().str(); + llvm::StringRef CPURef = TM.getTargetCPU(); + ImageBinary.StringData["triple"] = TripleStr; + ImageBinary.StringData["arch"] = CPURef; + llvm::SmallString<0> Buffer = OffloadBinary::write(ImageBinary); + if (Buffer.size() % OffloadBinary::getAlignment() != 0) + // Offload binary has invalid size alignment + return false; + OS2 << Buffer; + if (Error E = writeFile(HostOutPath, + StringRef(BinaryData.begin(), BinaryData.size()))) + return false; + return true; +} + +extern "C" bool LLVMRustOffloadEmbedBufferInModule(LLVMModuleRef HostM, + const char *HostOutPath) { + auto MBOrErr = MemoryBuffer::getFile(HostOutPath); + if (!MBOrErr) { + auto E = MBOrErr.getError(); + auto _B = errorCodeToError(E); + return false; + } + MemoryBufferRef Buf = (*MBOrErr)->getMemBufferRef(); + Module *M = unwrap(HostM); + StringRef SectionName = ".llvm.offloading"; + Align Alignment = Align(8); + llvm::embedBufferInModule(*M, Buf, SectionName, Alignment); + return true; +} + +// Clone OldFn into NewFn, remapping its arguments to RebuiltArgs. +// Each arg of OldFn is replaced with the corresponding value in RebuiltArgs. +// For scalars, RebuiltArgs contains the value cast and/or truncated to the +// original type. +extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn, + const LLVMValueRef *RebuiltArgs) { + llvm::Function *oldFn = llvm::unwrap(OldFn); + llvm::Function *newFn = llvm::unwrap(NewFn); + + // Map old arguments to new arguments. We skip the first dyn_ptr argument, + // since it can't be used directly by user code. + llvm::ValueToValueMapTy vmap; + auto newArgIt = newFn->arg_begin(); + newArgIt->setName("dyn_ptr"); + + unsigned i = 0; + for (auto &oldArg : oldFn->args()) { + vmap[&oldArg] = unwrap(RebuiltArgs[i++]); + } + + llvm::SmallVector returns; + llvm::CloneFunctionInto(newFn, oldFn, vmap, + llvm::CloneFunctionChangeType::LocalChangesOnly, + returns); + + BasicBlock &entry = newFn->getEntryBlock(); + BasicBlock &clonedEntry = *std::next(newFn->begin()); + + if (entry.getTerminator()) + entry.getTerminator()->eraseFromParent(); + + IRBuilder<> B(&entry); + B.CreateBr(&clonedEntry); +} diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 399f20ebfe1eb..2f4e5606cd555 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -180,7 +180,7 @@ decl_derive!( decl_derive!([Lift, attributes(lift)] => lift::lift_derive); decl_derive!( [Diagnostic, attributes( - // struct attributes + // struct and field attributes diag, help, help_once, @@ -194,7 +194,9 @@ decl_derive!( suggestion, suggestion_short, suggestion_hidden, - suggestion_verbose)] => diagnostics::diagnostic_derive + suggestion_verbose)] => + #[doc = "See "] + diagnostics::diagnostic_derive ); decl_derive!( [Subdiagnostic, attributes( diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f834784f98847..30ee1d945dc18 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -749,8 +749,7 @@ pub struct GlobalCtxt<'tcx> { /// Caches the results of goal evaluation in the new solver. pub new_solver_evaluation_cache: Lock>>, - pub new_solver_canonical_param_env_cache: - Lock, ty::CanonicalParamEnvCacheEntry>>>, + pub new_solver_canonical_param_env_cache: Lock>>, pub canonical_param_env_cache: CanonicalParamEnvCache<'tcx>, diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 470abf327679f..ecc8d8867a279 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -146,15 +146,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> { f(&mut *self.new_solver_evaluation_cache.lock()) } - fn canonical_param_env_cache_get_or_insert( + fn with_canonical_param_env_cache( self, - param_env: ty::ParamEnv<'tcx>, - f: impl FnOnce() -> ty::CanonicalParamEnvCacheEntry, - from_entry: impl FnOnce(&ty::CanonicalParamEnvCacheEntry) -> R, + f: impl FnOnce(&mut ty::CanonicalParamEnvCache) -> R, ) -> R { - let mut cache = self.new_solver_canonical_param_env_cache.lock(); - let entry = cache.entry(param_env).or_insert_with(f); - from_entry(entry) + f(&mut *self.new_solver_canonical_param_env_cache.lock()) } fn assert_evaluation_is_concurrent(&self) { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 3b6c38a17a625..0e1519f5a27f9 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1190,6 +1190,15 @@ impl<'tcx> Ty<'tcx> { matches!(self.kind(), Adt(..)) } + #[inline] + pub fn is_self_param(self) -> bool { + if let Param(param) = self.kind() { + param.index == 0 && param.name == kw::SelfUpper + } else { + false + } + } + #[inline] pub fn is_ref(self) -> bool { matches!(self.kind(), Ref(..)) diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index a0f38dcb50cb4..ff7cef3613437 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -37,7 +37,7 @@ pub struct TypeckResults<'tcx> { type_dependent_defs: ItemLocalMap>, /// Resolved definitions for splatted function calls. - splatted_defs: ItemLocalMap>, + splatted_defs: ItemLocalMap, ErrorGuaranteed>>, /// Resolved field indices for field accesses in expressions (`S { field }`, `obj.field`) /// or patterns (`S { field }`). The index is often useful by itself, but to learn more @@ -295,18 +295,20 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.type_dependent_defs } } - pub fn splatted_defs(&self) -> LocalTableInContext<'_, Result> { + pub fn splatted_defs( + &self, + ) -> LocalTableInContext<'_, Result, ErrorGuaranteed>> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.splatted_defs } } - pub fn splatted_def(&self, id: HirId) -> Option { + pub fn splatted_def(&self, id: HirId) -> Option> { validate_hir_id_for_typeck_results(self.hir_owner, id); self.splatted_defs.get(&id.local_id).cloned().and_then(|r| r.ok()) } pub fn splatted_defs_mut( &mut self, - ) -> LocalTableInContextMut<'_, Result> { + ) -> LocalTableInContextMut<'_, Result, ErrorGuaranteed>> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.splatted_defs } } @@ -431,7 +433,7 @@ impl<'tcx> TypeckResults<'tcx> { } pub fn is_splatted_call(&self, expr: &hir::Expr<'_>) -> bool { - matches!(self.splatted_defs().get(expr.hir_id), Some(Ok(SplattedDef { .. }))) + matches!(self.splatted_defs().get(expr.hir_id), Some(Ok(_))) } /// Returns the computed binding mode for a `PatKind::Binding` pattern @@ -598,14 +600,62 @@ impl<'tcx> TypeckResults<'tcx> { /// A resolved splatted function call. #[derive(Debug, Copy, Clone, PartialEq, Eq, StableHash, TyEncodable, TyDecodable)] -pub struct SplattedDef { - /// The function DefId, if available (FnPtrs don't have DefIds) - pub def_id: Option, - /// The index of the first argument in the callee's splatted tuple, and the index of the - /// splatted tuple argument in the caller. - pub arg_index: u16, - /// The number of arguments in the splatted tuple. - pub arg_count: u16, +pub enum SplattedDef<'tcx> { + /// A resolved FnDef call. + FnDef { + /// The DefId of the FnDef (used to look up its type). + def_id: DefId, + + /// The index of the first argument in the callee's splatted tuple, and the index of the + /// splatted tuple argument in the caller. + arg_index: u16, + + /// The number of arguments in the splatted tuple. + arg_count: u16, + }, + + /// A resolved FnPtr Call. + FnPtr { + /// The resolved type of the FnPtr. + fn_ptr_type: Ty<'tcx>, + + /// The index of the first argument in the callee's splatted tuple, and the index of the + /// splatted tuple argument in the caller. + arg_index: u16, + + /// The number of arguments in the splatted tuple. + arg_count: u16, + }, +} + +impl<'tcx> SplattedDef<'tcx> { + pub fn def_id(&self) -> Option { + match self { + SplattedDef::FnDef { def_id, .. } => Some(*def_id), + SplattedDef::FnPtr { .. } => None, + } + } + + pub fn fn_ptr_type(&self) -> Option> { + match self { + SplattedDef::FnDef { .. } => None, + SplattedDef::FnPtr { fn_ptr_type, .. } => Some(*fn_ptr_type), + } + } + + pub fn arg_index(&self) -> u16 { + match self { + SplattedDef::FnDef { arg_index, .. } => *arg_index, + SplattedDef::FnPtr { arg_index, .. } => *arg_index, + } + } + + pub fn arg_count(&self) -> u16 { + match self { + SplattedDef::FnDef { arg_count, .. } => *arg_count, + SplattedDef::FnPtr { arg_count, .. } => *arg_count, + } + } } /// Validate that the given HirId (respectively its `local_id` part) can be diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 70e9129ffee3f..69590fc351320 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -448,7 +448,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { let build_enabled = self .tcx .sess - .target_features + .internal_target_features .iter() .copied() .filter(|feature| missing.contains(feature)) diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 4b067a8ca79e2..fcf2432b4d8dc 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -28,6 +28,36 @@ use tracing::{debug, info, instrument, trace}; use crate::diagnostics::*; use crate::thir::cx::ThirBuildCx; +/// The receiver of a splatted method, or the expression for a splatted function call. +#[derive(Copy, Clone, Debug)] +enum SplattedFunc<'tcx> { + /// The expression for a method receiver. Always a FnDef. + FnDefReceiver(&'tcx hir::Expr<'tcx>), + /// The expression or path for a function call. + /// This can be a FnDef or FnPtr. + FnExpression(&'tcx hir::Expr<'tcx>), +} + +impl<'tcx> SplattedFunc<'tcx> { + fn has_receiver(&self) -> bool { + matches!(self, SplattedFunc::FnDefReceiver(_)) + } + + fn receiver(&self) -> Option<&'tcx hir::Expr<'tcx>> { + match self { + SplattedFunc::FnDefReceiver(receiver) => Some(receiver), + SplattedFunc::FnExpression(_fn_expression) => None, + } + } + + fn fn_expression(&self) -> Option<&'tcx hir::Expr<'tcx>> { + match self { + SplattedFunc::FnDefReceiver(_receiver) => None, + SplattedFunc::FnExpression(fn_expression) => Some(fn_expression), + } + } +} + fn parsed_attrs(id: HirId, tcx: TyCtxt<'_>) -> ThinVec { HasAttrs::get_attrs(id, &tcx) .into_iter() @@ -375,7 +405,12 @@ impl<'tcx> ThirBuildCx<'tcx> { if self.typeck_results.is_splatted_call(expr) { // The callee has a splatted tuple argument. // rewrite `receiver.f(a, u, v)` into `receiver.f(a, #[rustc_splat] (u, v))` - self.convert_splatted_callee(expr, fn_span, args, Some(receiver)) + self.convert_splatted_callee( + expr, + fn_span, + args, + SplattedFunc::FnDefReceiver(receiver), + ) } else { // Rewrite a.b(c) into UFCS form like Trait::b(a, c) let expr = self.method_callee(expr, segment.ident.span, None); @@ -425,7 +460,12 @@ impl<'tcx> ThirBuildCx<'tcx> { } else if self.typeck_results.is_splatted_call(expr) { // The callee has a splatted tuple argument. // rewrite `f(a, u, v)` into `f(a, #[rustc_splat] (u, v))` - self.convert_splatted_callee(expr, fun.span, args, None) + self.convert_splatted_callee( + expr, + fun.span, + args, + SplattedFunc::FnExpression(fun), + ) } else { // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. let adt_data = if let hir::ExprKind::Path(ref qpath) = fun.kind @@ -1224,95 +1264,42 @@ impl<'tcx> ThirBuildCx<'tcx> { } } - fn splatted_callee( - &mut self, - expr: &hir::Expr<'_>, - span: Span, - ) -> (Expr<'tcx>, u16 /* arg_index */, u16 /* arg_count */) { - let SplattedDef { def_id, arg_index, arg_count } = - self.typeck_results.splatted_def(expr.hir_id).unwrap_or_else(|| { - span_bug!(expr.span, "no splatted def for function or method callee") - }); - - let expr = if let Some(def_id) = def_id { - // We're calling a function via a FnDef, and its possibly generic type - let def_kind = self.tcx.def_kind(def_id); - let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(def_kind, def_id)); - debug!( - "splatted_callee FnDef: user_ty={:?} def_kind={:?} def_id={:?} arg_index={:?} arg_count={:?}", - user_ty, def_kind, def_id, arg_index, arg_count, - ); - - Expr { - temp_scope_id: expr.hir_id.local_id, - ty: self - .tcx - .type_of(def_id) - .instantiate(self.tcx, self.typeck_results.node_args(expr.hir_id)) - .skip_norm_wip(), - span, - kind: ExprKind::ZstLiteral { user_ty }, - } - } else { - // We're calling a function via a FnPtr and its type - // FIXME(splat): populate the side-tables for FnPtrs, using liberated_fn_sigs if needed - let fn_ty = self.typeck_results.expr_ty_adjusted(expr); - let user_ty = - self.typeck_results.user_provided_types().get(expr.hir_id).copied().map(Box::new); - debug!( - "splatted_callee FnPtr: user_ty={:?} fn_ty={:?} arg_index={:?} arg_count={:?}", - user_ty, fn_ty, arg_index, arg_count, - ); - - if !fn_ty.is_fn() { - span_bug!(expr.span, "splatted FnPtr side-tables are not yet implemented") - } - - Expr { - temp_scope_id: expr.hir_id.local_id, - // Create a new FnPtr FnSig type, representing the splatted function arguments with - // user-supplied generic types applied - ty: Ty::new_fn_ptr(self.tcx, fn_ty.fn_sig(self.tcx)), - span, - kind: ExprKind::ZstLiteral { user_ty }, - } - }; - - (expr, arg_index, arg_count) - } - /// The callee has a splatted tuple argument. /// Rewrite a splatted call `receiver.f(a, u, v)` into `receiver.f(a, #[rustc_splat] (u, v))`. /// The receiver is optional. fn convert_splatted_callee( &mut self, - expr: &hir::Expr<'_>, + call_expr: &'tcx hir::Expr<'_>, fn_span: Span, args: &'tcx [hir::Expr<'tcx>], - receiver: Option<&'tcx hir::Expr<'tcx>>, + receiver_or_func: SplattedFunc<'tcx>, ) -> ExprKind<'tcx> { let tcx = self.tcx; - // The callee has a splatted tuple argument. - let (func, tupled_arg_index, tupled_args_count) = self.splatted_callee(expr, fn_span); - let tupled_arg_index = usize::from(tupled_arg_index); - let tupled_args_count = usize::from(tupled_args_count); + // Look up the typeck results + let splatted_def = + self.typeck_results.splatted_def(call_expr.hir_id).unwrap_or_else(|| { + span_bug!(call_expr.span, "no splatted def for function or method callee") + }); + + let tupled_arg_index = usize::from(splatted_def.arg_index()); + let tupled_args_count = usize::from(splatted_def.arg_count()); // Splatting an empty tuple is permitted: `a.f() -> Trait::f(a, #[rustc_splat] ())`. // In that case, the tupled arg index is one past the end of the args. if tupled_arg_index + tupled_args_count > args.len() { span_bug!( - expr.span, - "splatted arg index out of bounds of function args: {:?} + {:?} > {:?} for function call: receiver {:?}, args {:?}", + call_expr.span, + "splatted arg index out of bounds of function args: {:?} + {:?} > {:?} for function call: {:?}, args {:?}", tupled_arg_index, tupled_args_count, args.len(), - receiver, + receiver_or_func, args, ); } - info!("Using splatted function span: {:?}", func.span); + debug!("Using splatted function span: {:?}", fn_span); // Split into non-tupled and tupled arguments let initial_non_tupled_args = @@ -1331,29 +1318,94 @@ impl<'tcx> ThirBuildCx<'tcx> { let tupled_arg_tys = tupled_args.iter().map(|e| self.typeck_results.expr_ty_adjusted(e)); - let temp_scope_id = - if receiver.is_some() { func.temp_scope_id } else { expr.hir_id.local_id }; + // We need the tupled arguments in HIR/MIR for type checking + // FIXME(splat): de-tuple args in codegen for performance let tupled_args = Expr { ty: Ty::new_tup_from_iter(tcx, tupled_arg_tys), - temp_scope_id, - span: expr.span, + temp_scope_id: call_expr.hir_id.local_id, + span: call_expr.span, kind: ExprKind::Tuple { fields: self.mirror_exprs(tupled_args) }, }; let tupled_args = self.thir.exprs.push(tupled_args); - let mut args = - if let Some(receiver) = receiver { vec![self.mirror_expr(receiver)] } else { vec![] }; + // Handle the receiver as the first arg, if present + let mut args = Vec::with_capacity( + usize::from(receiver_or_func.has_receiver()) + + initial_non_tupled_args.len() + + 1 + + final_non_tupled_args.len(), + ); + if let Some(receiver) = receiver_or_func.receiver() { + args.push(self.mirror_expr(receiver)); + } args.extend(initial_non_tupled_args); args.push(tupled_args); args.extend(final_non_tupled_args); - // We need the tupled arguments in HIR/MIR for type checking, but codegen can - // de-tuple them for performance - let fn_span = if receiver.is_some() { func.span } else { expr.span }; + let fn_span = if receiver_or_func.has_receiver() { fn_span } else { call_expr.span }; + + let (fn_ty, fun_expr) = match (splatted_def, receiver_or_func.fn_expression()) { + // Create a FnDef shim for user-provided types + (SplattedDef::FnDef { def_id, arg_index, arg_count }, _) => { + // We're calling a function via a FnDef, and its possibly generic type + // This is effectively `self.method_callee(call_expr, fn_span, None)`, + // applied to `splatted_def` instead of `type_dependent_def`. + let def_kind = self.tcx.def_kind(def_id); + let user_ty = + self.user_args_applied_to_res(call_expr.hir_id, Res::Def(def_kind, def_id)); + debug!( + "splatted_callee FnDef: user_ty={:?} def_kind={:?} def_id={:?} arg_index={:?} arg_count={:?}", + user_ty, def_kind, def_id, arg_index, arg_count, + ); + + // Create a new FnDef expression with user-provided type applied + let callee_expr = Expr { + temp_scope_id: call_expr.hir_id.local_id, + ty: self + .tcx + .type_of(def_id) + .instantiate(self.tcx, self.typeck_results.node_args(call_expr.hir_id)) + .skip_norm_wip(), + span: fn_span, + kind: ExprKind::ZstLiteral { user_ty }, + }; + (callee_expr.ty, self.thir.exprs.push(callee_expr)) + } + + // We're calling a function via a FnPtr and its type + // FIXME(splat): do we need to populate and apply user_provided_types() ? + (SplattedDef::FnPtr { fn_ptr_type, arg_index, arg_count }, Some(fn_expression)) => { + debug!( + "splatted_callee FnPtr: fn_ty={:?} arg_index={:?} arg_count={:?}", + fn_ptr_type, arg_index, arg_count, + ); + + if !fn_ptr_type.is_fn() { + span_bug!( + call_expr.span, + "splatted FnPtr side-tables were not populated correctly, non-fn type received: {:?}", + fn_ptr_type + ) + } + + // Pass through the FnPtr type and the mirrored function path + (fn_ptr_type, self.mirror_expr(fn_expression)) + } + // FnPtrs must have a function expression (and they never have method receivers) + (SplattedDef::FnPtr { .. }, None) => { + span_bug!( + call_expr.span, + "convert_splatted_callee: FnPtr without fn expression (or with receiver) is invalid: splatted_def={:?}, receiver_or_func={:?}", + splatted_def, + receiver_or_func, + ); + } + }; + ExprKind::Call { - ty: func.ty, - fun: self.thir.exprs.push(func), + ty: fn_ty, + fun: fun_expr, args: args.into_boxed_slice(), from_hir_call: true, fn_span, diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index da2ea948366db..5c77827a67165 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -23,12 +23,6 @@ use crate::{Analysis, Backward, GenKill}; /// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis pub struct MaybeLiveLocals; -impl MaybeLiveLocals { - pub fn transfer_function(state: &mut I) -> TransferFunction<'_, I> { - TransferFunction(state) - } -} - impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { type Domain = DenseBitSet; type Direction = Backward; @@ -50,7 +44,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { statement: &mir::Statement<'tcx>, location: Location, ) { - TransferFunction(state).visit_statement(statement, location); + LivenessTransferFunction(state).visit_statement(statement, location); } fn apply_primary_terminator_effect( @@ -59,7 +53,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { terminator: &mir::Terminator<'tcx>, location: Location, ) { - TransferFunction(state).visit_terminator(terminator, location); + LivenessTransferFunction(state).visit_terminator(terminator, location); } fn apply_call_return_effect( @@ -84,9 +78,9 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { } } -pub struct TransferFunction<'a, I>(pub &'a mut I); +pub struct LivenessTransferFunction<'a, I>(pub &'a mut I); -impl<'tcx, I> Visitor<'tcx> for TransferFunction<'_, I> +impl<'tcx, I> Visitor<'tcx> for LivenessTransferFunction<'_, I> where I: GenKill, { @@ -136,7 +130,6 @@ impl<'tcx> Visitor<'tcx> for YieldResumeEffect<'_> { } } -#[derive(Eq, PartialEq, Clone)] pub enum DefUse { /// Full write to the local. Def, @@ -208,7 +201,8 @@ impl DefUse { } } -/// Like `MaybeLiveLocals`, but does not mark locals as live if they are used in a dead assignment. +/// Like `MaybeLiveLocals` (and layered on top of `MaybeLiveLocals`), but does not mark locals as +/// live if they are used in a dead assignment. /// /// This is basically written for dead store elimination and nothing else. /// @@ -219,7 +213,7 @@ pub struct MaybeTransitiveLiveLocals<'a> { } impl<'a> MaybeTransitiveLiveLocals<'a> { - /// The `always_alive` set is the set of locals to which all stores should unconditionally be + /// The `always_live` set is the set of locals to which all stores should unconditionally be /// considered live. /// /// This should include at least all locals that are ever borrowed. @@ -233,7 +227,7 @@ impl<'a> MaybeTransitiveLiveLocals<'a> { pub fn can_be_removed_if_dead<'tcx>( stmt_kind: &StatementKind<'tcx>, always_live: &DenseBitSet, - debuginfo_locals: &'a DenseBitSet, + debuginfo_locals: &DenseBitSet, ) -> Option> { // Compute the place that we are storing to, if any let destination = match stmt_kind { @@ -274,12 +268,11 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { const NAME: &'static str = "transitive liveness"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = not live - DenseBitSet::new_empty(body.local_decls.len()) + MaybeLiveLocals.bottom_value(body) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { - // No variables are live until we observe a use + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { + MaybeLiveLocals.initialize_start_block(body, state) } fn apply_primary_statement_effect( @@ -288,14 +281,16 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { statement: &mir::Statement<'tcx>, location: Location, ) { + // This is the one part of `MaybeTransitiveLiveLocals` that differs from `MaybeLiveLocals`. if let Some(destination) = - Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals) + Self::can_be_removed_if_dead(&statement.kind, self.always_live, self.debuginfo_locals) && !state.contains(destination.local) { // This store is dead return; } - TransferFunction(state).visit_statement(statement, location); + + MaybeLiveLocals.apply_primary_statement_effect(state, statement, location); } fn apply_primary_terminator_effect( @@ -304,27 +299,15 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { terminator: &mir::Terminator<'tcx>, location: Location, ) { - TransferFunction(state).visit_terminator(terminator, location); + MaybeLiveLocals.apply_primary_terminator_effect(state, terminator, location) } fn apply_call_return_effect( &self, state: &mut Self::Domain, - _block: mir::BasicBlock, + block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, ) { - if let CallReturnPlaces::Yield(resume_place) = return_places { - YieldResumeEffect(state).visit_place( - &resume_place, - PlaceContext::MutatingUse(MutatingUseContext::Yield), - Location::START, - ) - } else { - return_places.for_each(|place| { - if let Some(local) = place.as_local() { - state.remove(local); - } - }); - } + MaybeLiveLocals.apply_call_return_effect(state, block, return_places); } } diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 6d573e1c00e1c..1e12e41ce1fb4 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -9,8 +9,7 @@ pub use self::initialized::{ MaybeUninitializedPlaces, MaybeUninitializedPlacesDomain, }; pub use self::liveness::{ - DefUse, MaybeLiveLocals, MaybeTransitiveLiveLocals, - TransferFunction as LivenessTransferFunction, + DefUse, LivenessTransferFunction, MaybeLiveLocals, MaybeTransitiveLiveLocals, }; pub use self::storage_liveness::{ MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive, always_storage_live_locals, diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index e392f856696be..924125404a07a 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -144,7 +144,7 @@ use rustc_index::{IndexVec, newtype_index}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, VisitPlacesWith, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals}; +use rustc_mir_dataflow::impls::{DefUse, LivenessTransferFunction, MaybeLiveLocals}; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::{Analysis, EntryStates, GenKill}; use tracing::{debug, trace}; @@ -619,7 +619,7 @@ fn save_as_intervals<'tcx>( state.current = state.current + 1; debug_assert_eq!(state.current, two_step_loc(loc, Effect::Before)); - MaybeLiveLocals::transfer_function(&mut state).visit_terminator(term, loc); + LivenessTransferFunction(&mut state).visit_terminator(term, loc); for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() { let loc = Location { block, statement_index }; @@ -659,7 +659,7 @@ fn save_as_intervals<'tcx>( // the all the writes we manually marked as live in the second half of the statement. state.current = TwoStepIndex::from_u32(state.current.as_u32() + 1); debug_assert_eq!(state.current, two_step_loc(loc, Effect::Before)); - MaybeLiveLocals::transfer_function(&mut state).visit_statement(stmt, loc); + LivenessTransferFunction(&mut state).visit_statement(stmt, loc); } // Cleanup the current block for the next one. diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index c971769d528bb..ae2028f1c62ea 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -321,6 +321,8 @@ impl<'tcx> Validator<'_, 'tcx> { // can only promote static accesses inside statics. && let Some(hir::ConstContext::Static(..)) = self.const_kind && !self.tcx.is_thread_local_static(did) + // Extern statics can never be read by CTFE, even inside a static. + && !self.tcx.is_foreign_item(did) { // Recurse. } else { diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index be1a7e1419a29..085a978b9956d 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -203,8 +203,23 @@ fn build_adrop_for_coroutine_shim<'tcx>( let ty::Coroutine(coroutine_def_id, impl_args) = impl_ty.kind() else { bug!("build_adrop_for_coroutine_shim not for coroutine impl type: ({:?})", shim); }; + let ty::Coroutine(_, id_args) = *tcx.type_of(*coroutine_def_id).skip_binder().kind() else { + bug!() + }; let source_info = SourceInfo::outermost(span); - let body = tcx.optimized_mir(*coroutine_def_id).future_drop_poll().unwrap(); + + // If the kind tys differ, we must use the by-move body + let def_id = if id_args.as_coroutine().kind_ty() == impl_args.as_coroutine().kind_ty() { + *coroutine_def_id + } else { + assert_eq!( + impl_args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap(), + ty::ClosureKind::FnOnce + ); + + tcx.coroutine_by_move_body_def_id(*coroutine_def_id) + }; + let body = tcx.optimized_mir(def_id).future_drop_poll().unwrap(); let mut body: Body<'tcx> = EarlyBinder::bind(tcx, body.clone()).instantiate(tcx, impl_args).skip_norm_wip(); body.source.instance = ty::InstanceKind::Shim(shim); diff --git a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs index 4479ce2dba08b..173595de5c8c2 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -57,7 +57,7 @@ fn do_check_simd_vector_abi<'tcx>( ) { let codegen_attrs = tcx.codegen_fn_attrs(def_id); let have_feature = |feat: Symbol| { - let target_feats = tcx.sess.unstable_target_features.contains(&feat); + let target_feats = tcx.sess.internal_target_features.contains(&feat); let fn_feats = codegen_attrs.target_features.iter().any(|x| x.name == feat); target_feats || fn_feats }; diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 20402649ceabd..73d969a398198 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -1,3 +1,5 @@ +use std::collections::hash_map::Entry; + use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack}; use rustc_type_ir::inherent::*; use rustc_type_ir::solve::{Goal, QueryInput}; @@ -83,23 +85,25 @@ pub(super) struct Canonicalizer<'a, D: SolverDelegate, I: Interner } impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { - pub(super) fn canonicalize_response>( - delegate: &'a D, - max_input_universe: ty::UniverseIndex, - value: T, - ) -> ty::Canonical { - let mut canonicalizer = Canonicalizer { + fn new(delegate: &'a D, canonicalize_mode: CanonicalizeMode) -> Self { + Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Response { max_input_universe }, - + canonicalize_mode, variables: Default::default(), variable_lookup_table: Default::default(), sub_root_lookup_table: Default::default(), var_kinds: Default::default(), - cache: Default::default(), - }; + } + } + pub(super) fn canonicalize_response>( + delegate: &'a D, + max_input_universe: ty::UniverseIndex, + value: T, + ) -> ty::Canonical { + let mut canonicalizer = + Canonicalizer::new(delegate, CanonicalizeMode::Response { max_input_universe }); let value = if value.has_type_flags(NEEDS_CANONICAL) { value.fold_with(&mut canonicalizer) } else { @@ -111,84 +115,80 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { Canonical { max_universe, var_kinds, value } } - fn canonicalize_param_env( - delegate: &'a D, - param_env: I::ParamEnv, - ) -> ( - I::ParamEnv, - ThinVec, - Vec>, - HashMap, - ) { + // The return value is the canonicalized `param_env`, plus a canonicalizer suitable for + // canonicalizing the rest of the input. (For efficiency, and when appropriate, the returned + // canonicalizer will be the same one used on `param_env`, with suitable modifications.) + fn canonicalize_param_env(delegate: &'a D, param_env: I::ParamEnv) -> (I::ParamEnv, Self) { if !param_env.has_type_flags(NEEDS_CANONICAL) { - return (param_env, ThinVec::new(), Vec::new(), Default::default()); + let rest_canonicalizer = Canonicalizer::new( + delegate, + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate), + ); + + return (param_env, rest_canonicalizer); } + // Do the `env` canonicalization, and then convert the canonicalizer to `rest` form for + // subsequent use. + let do_env_and_make_rest = || { + let mut env_canonicalizer = Canonicalizer::new( + delegate, + CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), + ); + let param_env = param_env.fold_with(&mut env_canonicalizer); + + // We do not reuse the cache as it may contain entries whose canonicalized + // value contains `'static`. While we could alternatively handle this by + // checking for `'static` when using cached entries, this does not + // feel worth the effort. I do not expect that a `ParamEnv` will ever + // contain large enough types for caching to be necessary. + debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty()); + let rest_canonicalizer = Canonicalizer { + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate), + cache: Default::default(), + ..env_canonicalizer + }; + + (param_env, rest_canonicalizer) + }; + // Check whether we can use the global cache for this param_env. As we only use // the `param_env` itself as the cache key, considering any additional information - // durnig its canonicalization would be incorrect. We always canonicalize region + // during its canonicalization would be incorrect. We always canonicalize region // inference variables in a separate universe, so these are fine. However, we do // track the universe of type and const inference variables so these must not be // globally cached. We don't rely on any additional information when canonicalizing // placeholders. if !param_env.has_non_region_infer() { - delegate.cx().canonical_param_env_cache_get_or_insert( - param_env, - || { - let mut env_canonicalizer = Canonicalizer { - delegate, - canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), - - variables: Default::default(), - variable_lookup_table: Default::default(), - sub_root_lookup_table: Default::default(), - var_kinds: Default::default(), - - cache: Default::default(), - }; - let param_env = param_env.fold_with(&mut env_canonicalizer); - debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty()); - CanonicalParamEnvCacheEntry { + delegate.cx().with_canonical_param_env_cache(|cache| match cache.0.entry(param_env) { + Entry::Vacant(e) => { + // Cache miss. Do `env` canonicalization and get `rest_canonicalizer`, and + // fill in the cache entry. + let (param_env, rest_canonicalizer) = do_env_and_make_rest(); + e.insert(CanonicalParamEnvCacheEntry { param_env, - variable_lookup_table: env_canonicalizer.variable_lookup_table, - var_kinds: env_canonicalizer.var_kinds, - variables: env_canonicalizer.variables, - } - }, - |&CanonicalParamEnvCacheEntry { - param_env, - variables: ref cache_variables, - ref variable_lookup_table, - ref var_kinds, - }| { - // FIXME(nnethercote): for reasons I don't understand, this `new`+`extend` - // combination is faster than `variables.clone()`, because it somehow avoids - // some allocations. - let mut variables = ThinVec::new(); - variables.extend(cache_variables.iter().copied()); - (param_env, variables, var_kinds.clone(), variable_lookup_table.clone()) - }, - ) + variables: rest_canonicalizer.variables.clone(), + var_kinds: rest_canonicalizer.var_kinds.clone(), + variable_lookup_table: rest_canonicalizer.variable_lookup_table.clone(), + }); + (param_env, rest_canonicalizer) + } + Entry::Occupied(e) => { + // Cache hit; no canonicalization required. Just set up `rest_canonicalizer`. + let e = e.get(); + let mut rest_canonicalizer = Canonicalizer::new( + delegate, + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate), + ); + rest_canonicalizer.variables.extend(e.variables.iter().copied()); + rest_canonicalizer.var_kinds.clone_from(&e.var_kinds); + rest_canonicalizer.variable_lookup_table.clone_from(&e.variable_lookup_table); + (e.param_env, rest_canonicalizer) + } + }) } else { - let mut env_canonicalizer = Canonicalizer { - delegate, - canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), - - variables: Default::default(), - variable_lookup_table: Default::default(), - sub_root_lookup_table: Default::default(), - var_kinds: Default::default(), - - cache: Default::default(), - }; - let param_env = param_env.fold_with(&mut env_canonicalizer); - debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty()); - ( - param_env, - env_canonicalizer.variables, - env_canonicalizer.var_kinds, - env_canonicalizer.variable_lookup_table, - ) + // Do `env` canonicalization and get `rest_canonicalizer`. + do_env_and_make_rest() } } @@ -204,27 +204,10 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { delegate: &'a D, input: QueryInput, ) -> (ThinVec, ty::Canonical>) { - // First canonicalize the `param_env` while keeping `'static` - let (param_env, variables, var_kinds, variable_lookup_table) = - Canonicalizer::canonicalize_param_env(delegate, input.goal.param_env); - // Then canonicalize the rest of the input without keeping `'static` - // while *mostly* reusing the canonicalizer from above. - let mut rest_canonicalizer = Canonicalizer { - delegate, - canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate), - - variables, - variable_lookup_table, - sub_root_lookup_table: Default::default(), - var_kinds, - - // We do not reuse the cache as it may contain entries whose canonicalized - // value contains `'static`. While we could alternatively handle this by - // checking for `'static` when using cached entries, this does not - // feel worth the effort. I do not expect that a `ParamEnv` will ever - // contain large enough types for caching to be necessary. - cache: Default::default(), - }; + // First canonicalize the `param_env` while keeping `'static`. This produces a + // canonicalizer that can canonicalize the rest of the input without keeping `'static`. + let (param_env, mut rest_canonicalizer) = + Self::canonicalize_param_env(delegate, input.goal.param_env); let predicate = input.goal.predicate; let predicate = predicate.fold_with(&mut rest_canonicalizer); diff --git a/compiler/rustc_resolve/src/diagnostics/mod.rs b/compiler/rustc_resolve/src/diagnostics/mod.rs index cadfab22c8862..9053d45a41191 100644 --- a/compiler/rustc_resolve/src/diagnostics/mod.rs +++ b/compiler/rustc_resolve/src/diagnostics/mod.rs @@ -1503,30 +1503,6 @@ pub(crate) struct RedundantImportVisibility { pub max_vis: String, } -#[derive(Diagnostic)] -#[diag("unknown diagnostic attribute")] -pub(crate) struct UnknownDiagnosticAttribute { - #[subdiagnostic] - pub help: Option, -} - -#[derive(Subdiagnostic)] -pub(crate) enum UnknownDiagnosticAttributeHelp { - #[suggestion( - "an attribute with a similar name exists", - style = "verbose", - code = "{typo_name}", - applicability = "machine-applicable" - )] - Typo { - #[primary_span] - span: Span, - typo_name: Symbol, - }, - #[help("add `#![feature({$feature})]` to the crate attributes to enable")] - UseFeature { feature: Symbol }, -} - // FIXME: Make this properly translatable. pub(crate) struct Ambiguity { pub ident: Ident, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 6921d0ed595fe..9747ebd4a8af8 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -25,11 +25,9 @@ use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; use rustc_session::diagnostics::feature_err; use rustc_session::lint::builtin::{ - LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, - UNUSED_MACRO_RULES, UNUSED_MACROS, + LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, UNUSED_MACRO_RULES, UNUSED_MACROS, }; use rustc_span::def_id::ModId; -use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::{self, AstPass, ExpnData, ExpnKind, LocalExpnId, MacroKind}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; @@ -742,60 +740,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span, msg).emit(); } - const DIAGNOSTIC_ATTRIBUTES: &[(Symbol, Option)] = &[ - (sym::on_unimplemented, None), - (sym::do_not_recommend, None), - (sym::on_move, Some(sym::diagnostic_on_move)), - (sym::on_const, Some(sym::diagnostic_on_const)), - (sym::on_unknown, Some(sym::diagnostic_on_unknown)), - (sym::on_unmatched_args, Some(sym::diagnostic_on_unmatched_args)), - (sym::on_type_error, Some(sym::diagnostic_on_type_error)), - (sym::opaque, Some(sym::diagnostic_opaque)), - ]; - - if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) - && let [namespace, attribute, ..] = &*path.segments - && namespace.ident.name == sym::diagnostic - && !DIAGNOSTIC_ATTRIBUTES.iter().any(|(attr, feature)| { - attribute.ident.name == *attr && feature.is_none_or(|f| self.features.enabled(f)) - }) - { - let name = attribute.ident.name; - let span = attribute.span(); - - let help = 'help: { - if self.tcx.sess.is_nightly_build() { - for (attr, feature) in DIAGNOSTIC_ATTRIBUTES { - if let Some(feature) = *feature - && *attr == name - { - break 'help Some( - diagnostics::UnknownDiagnosticAttributeHelp::UseFeature { feature }, - ); - } - } - } - - let candidates = DIAGNOSTIC_ATTRIBUTES - .iter() - .filter_map(|(attr, feature)| { - feature.is_none_or(|f| self.features.enabled(f)).then_some(*attr) - }) - .collect::>(); - - find_best_match_for_name(&candidates, name, None).map(|typo_name| { - diagnostics::UnknownDiagnosticAttributeHelp::Typo { span, typo_name } - }) - }; - - self.tcx.sess.psess.buffer_lint( - UNKNOWN_DIAGNOSTIC_ATTRIBUTES, - span, - node_id, - diagnostics::UnknownDiagnosticAttribute { help }, - ); - } - Ok((ext, res)) } diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 84a26af6b54ce..e5c874503a00f 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -304,7 +304,7 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { } } - if !sess.target.singlethread(&sess.target_features) { + if !sess.target.singlethread(&sess.internal_target_features) { ins_none!(sym::target_has_threads); } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 834424b8fbd84..f30d825b470ac 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -371,11 +371,11 @@ pub struct Session { /// Architecture to use for interpreting asm!. pub asm_arch: Option, - /// Set of enabled features for the current target. - pub target_features: FxIndexSet, - - /// Set of enabled features for the current target, including unstable ones. - pub unstable_target_features: FxIndexSet, + /// Set of actually enabled features for the current target, including ones that are not + /// in `cfg(target_feature)` because they are unstable or internal-only. + /// This is used by the compiler itself when it needs to know which target features are actually + /// going to be enabled in the backend. + pub internal_target_features: FxIndexSet, /// The version of the rustc process, possibly including a commit hash and description. pub cfg_version: &'static str, @@ -1341,8 +1341,7 @@ pub fn build_session( ctfe_backtrace, miri_unleashed_features: Lock::new(Default::default()), asm_arch, - target_features: Default::default(), - unstable_target_features: Default::default(), + internal_target_features: Default::default(), cfg_version, using_internal_features, env_depinfo: Default::default(), diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 47c933e245d49..80d1bae71ae89 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -174,6 +174,18 @@ struct SourceMapFiles { stable_id_to_source_file: UnhashMap>, } +impl std::fmt::Debug for SourceMapFiles { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let SourceMapFiles { source_files, stable_id_to_source_file: _ } = self; + + f.debug_list() + .entries( + source_files.iter().map(|f| f.name.prefer_remapped_unconditionally().to_string()), + ) + .finish() + } +} + /// Used to construct a `SourceMap` with `SourceMap::with_inputs`. pub struct SourceMapInputs { pub file_loader: Box, @@ -203,6 +215,28 @@ pub struct SourceMap { checksum_hash_kind: Option, } +impl std::fmt::Debug for SourceMap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let SourceMap { + files, + file_loader, + path_mapping, + working_dir, + hash_kind, + checksum_hash_kind, + } = self; + + f.debug_struct("SourceMap") + .field("files", files) + .field("file_loader", &format_args!("")) + .field("path_mapping", path_mapping) + .field("working_dir", working_dir) + .field("hash_kind", hash_kind) + .field("checksum_hash_kind", checksum_hash_kind) + .finish() + } +} + impl SourceMap { pub fn new(path_mapping: FilePathMapping) -> SourceMap { Self::with_inputs(SourceMapInputs { @@ -1117,7 +1151,7 @@ pub fn get_source_map() -> Option> { with_session_globals(|session_globals| session_globals.source_map.clone()) } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FilePathMapping { mapping: Vec<(PathBuf, PathBuf)>, filename_remapping_scopes: RemapPathScopeComponents, diff --git a/compiler/rustc_target/src/spec/base/l4re.rs b/compiler/rustc_target/src/spec/base/l4re.rs index 8722c8a71e23a..cc67bd6d3a487 100644 --- a/compiler/rustc_target/src/spec/base/l4re.rs +++ b/compiler/rustc_target/src/spec/base/l4re.rs @@ -1,14 +1,59 @@ -use crate::spec::{Cc, Env, LinkerFlavor, Os, PanicStrategy, RelocModel, TargetOptions, cvs}; +use crate::spec::{ + Cc, Env, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFlavor, + Os, PanicStrategy, TargetOptions, add_link_args, crt_objects, cvs, +}; pub(crate) fn opts() -> TargetOptions { + // add ld- and cc-style args + macro_rules! prepare_args { + ($($val:expr),+) => {{ + let ld_args = &[$($val),+]; + let cc_args = &[$(concat!("-Wl,", $val)),+]; + + let mut ret = TargetOptions::link_args(LinkerFlavor::Unix(Cc::No), ld_args); + add_link_args(&mut ret, LinkerFlavor::Unix(Cc::Yes), cc_args); + ret + }}; + } + + let pre_link_args = prepare_args!("-nostdlib", "-dynamic-linker=rom/libld-l4.so"); + + let late_link_args = prepare_args!("-lc", "-lgcc_eh"); + + let pre_link_objects_self_contained = crt_objects::new(&[ + (LinkOutputKind::StaticNoPicExe, &["crt1.o", "crti.o", "crtbeginT.o"]), + (LinkOutputKind::StaticPicExe, &["crt1.p.o", "crti.o", "crtbegin.o"]), + (LinkOutputKind::DynamicNoPicExe, &["crt1.o", "crti.o", "crtbegin.o"]), + (LinkOutputKind::DynamicPicExe, &["crt1.s.o", "crti.o", "crtbeginS.o"]), + (LinkOutputKind::DynamicDylib, &["crti.s.o", "crtbeginS.o"]), + (LinkOutputKind::StaticDylib, &["crti.s.o", "crtbeginS.o"]), + ]); + + let post_link_objects_self_contained = crt_objects::new(&[ + (LinkOutputKind::StaticNoPicExe, &["crtendT.o", "crtn.o"]), + (LinkOutputKind::StaticPicExe, &["crtend.o", "crtn.o"]), + (LinkOutputKind::DynamicNoPicExe, &["crtend.o", "crtn.o"]), + (LinkOutputKind::DynamicPicExe, &["crtendS.o", "crtn.o"]), + (LinkOutputKind::DynamicDylib, &["crtendS.o", "crtn.s.o"]), + (LinkOutputKind::StaticDylib, &["crtendS.o", "crtn.s.o"]), + ]); + TargetOptions { os: Os::L4Re, env: Env::Uclibc, - linker_flavor: LinkerFlavor::Unix(Cc::No), - panic_strategy: PanicStrategy::Abort, - linker: Some("l4-bender".into()), families: cvs!["unix"], - relocation_model: RelocModel::Static, + panic_strategy: PanicStrategy::Abort, + linker_flavor: LinkerFlavor::Unix(Cc::No), + dynamic_linking: true, + position_independent_executables: true, + has_thread_local: true, + pre_link_args, + late_link_args, + pre_link_objects_self_contained, + post_link_objects_self_contained, + link_self_contained: LinkSelfContainedDefault::WithComponents( + LinkSelfContainedComponents::LIBC | LinkSelfContainedComponents::CRT_OBJECTS, + ), ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 1f17173953643..97c8db0780733 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1569,6 +1569,7 @@ supported_targets! { ("avr-none", avr_none), + ("aarch64-unknown-l4re-uclibc", aarch64_unknown_l4re_uclibc), ("x86_64-unknown-l4re-uclibc", x86_64_unknown_l4re_uclibc), ("aarch64-unknown-redox", aarch64_unknown_redox), @@ -3835,7 +3836,7 @@ impl Target { pub fn object_architecture( &self, - unstable_target_features: &FxIndexSet, + internal_target_features: &FxIndexSet, ) -> Option<(object::Architecture, Option)> { use object::Architecture; Some(match self.arch { @@ -3878,7 +3879,7 @@ impl Target { Arch::RiscV32 => (Architecture::Riscv32, None), Arch::RiscV64 => (Architecture::Riscv64, None), Arch::Sparc => { - if unstable_target_features.contains(&sym::v8plus) { + if internal_target_features.contains(&sym::v8plus) { // Target uses V8+, aka EM_SPARC32PLUS, aka 64-bit V9 but in 32-bit mode (Architecture::Sparc32Plus, None) } else { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_l4re_uclibc.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_l4re_uclibc.rs new file mode 100644 index 0000000000000..bca1195ddad72 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_l4re_uclibc.rs @@ -0,0 +1,28 @@ +use crate::spec::{Arch, Cc, LinkerFlavor, Target, TargetOptions, base}; + +pub(crate) fn target() -> Target { + let mut base = base::l4re::opts(); + + let extra_link_args = &["-zmax-page-size=0x1000", "-zcommon-page-size=0x1000"]; + base.add_pre_link_args(LinkerFlavor::Unix(Cc::Yes), extra_link_args); + base.add_pre_link_args(LinkerFlavor::Unix(Cc::No), extra_link_args); + + Target { + llvm_target: "aarch64-unknown-l4re-uclibc".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Arm64 L4Re".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: Arch::AArch64, + options: TargetOptions { + features: "+v8a".into(), + mcount: "__mcount".into(), + max_atomic_width: Some(128), + ..base + } + } +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs index 5ab6b094dfa06..7030a98305a0b 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs @@ -1,11 +1,13 @@ -use crate::spec::{Arch, PanicStrategy, Target, TargetMetadata, base}; +use crate::spec::{Arch, Cc, LinkerFlavor, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut base = base::l4re::opts(); base.cpu = "x86-64".into(); base.plt_by_default = false; base.max_atomic_width = Some(64); - base.panic_strategy = PanicStrategy::Abort; + let extra_link_args = &["-zmax-page-size=0x1000", "-zcommon-page-size=0x1000"]; + base.add_pre_link_args(LinkerFlavor::Unix(Cc::Yes), extra_link_args); + base.add_pre_link_args(LinkerFlavor::Unix(Cc::No), extra_link_args); Target { llvm_target: "x86_64-unknown-l4re-gnu".into(), diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index ce09972396e56..f1dd2d8191985 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -28,8 +28,8 @@ //! call ABI. For example, disabling the `x87` feature on x86 changes how scalar floats are passed as //! arguments, so letting people toggle that feature would be unsound. To this end, the //! [`Target::abi_required_features`] function computes which target features must and must not be -//! enabled for any given target, and individual features can also be marked as [`Forbidden`]. See -//! for some more context. +//! enabled for any given target, and individual features can also be marked as [`InternalOnly`]. +//! See for some more context. //! //! The one exception to features that change the ABI is features that enable larger vector //! registers. Those are permitted to be listed here. The `*_FOR_CORRECT_VECTOR_ABI` arrays store @@ -45,7 +45,8 @@ use rustc_span::{Symbol, sym}; use crate::spec::{Arch, FloatAbi, LlvmAbi, RustcAbi, Target}; -/// Features that control behaviour of rustc, rather than the codegen. +/// Features that control behaviour of rustc, rather than the codegen. Not to be included in +/// `cfg(target_feature)`, `sess.internal_target_features`, or the backend's feature list. /// These exist globally and are not in the target-specific lists below. pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; @@ -69,17 +70,21 @@ pub enum Stability { /// feature gate! Symbol, ), + /// This is not actually something we expose as a "target feature" to our users. + /// We just manage it internally as a target feature since that's how LLVM represents it. /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be /// set in the target spec. It is never set in `cfg(target_feature)`. Used in particular for /// features are actually ABI configuration flags (such as "soft-float" on many targets). - /// However, "forbidden" target features can still sometimes be enabled via `-Ctarget-cpu` or - /// target feature implications (on the Rust/LLVM level). To prevent that, ABI-relevant target - /// features are ideally pinned down (required or forbidden) in - /// [`Target::abi_required_features`]. - Forbidden { + /// + /// However, "internal" target features can still sometimes be enabled or disabled via + /// `-Ctarget-cpu` or Rust/LLVM target feature implications. Make sure nothing implies this + /// target feature and nothing is implied by this target feature (except for other internal-only + /// features). Ideally, ABI-relevant target features are pinned down (marked as required or + /// incompatible) in [`Target::abi_required_features`]. + InternalOnly { reason: &'static str, /// True if this is always an error, false if this can be reported as a warning when set via - /// `-Ctarget-feature`. + /// `-Ctarget-feature` (and a hard error when set via `#[target_feature]`). hard_error: bool, }, } @@ -120,7 +125,9 @@ impl Stability { } } Stability::Stable { .. } => None, - Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"), + Stability::InternalOnly { .. } => { + panic!("internal-only features should not reach this far") + } } } @@ -138,7 +145,7 @@ impl Stability { Stability::Unstable(_) | Stability::CfgStableToggleUnstable(_) | Stability::Stable { .. } => Ok(()), - Stability::Forbidden { reason, hard_error: _ } => Err(reason), + Stability::InternalOnly { reason, hard_error: _ } => Err(reason), } } } @@ -157,7 +164,8 @@ static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("aes", Unstable(sym::arm_target_feature), &["neon"]), ( "atomics-32", - Stability::Forbidden { + // Not implied by any CPU model or other feature. + Stability::InternalOnly { reason: "unsound because it changes the ABI of atomic operations", hard_error: false, }, @@ -244,7 +252,8 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // We forbid directly toggling just `fp-armv8`; it must be toggled with `neon`. ( "fp-armv8", - Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`", hard_error: false }, + // Pinned down by [`Target::abi_required_features`] when needed. + Stability::InternalOnly { reason: "Rust ties `fp-armv8` to `neon`", hard_error: false }, &[], ), // FEAT_FP8 @@ -311,7 +320,8 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rdm", Stable, &["neon"]), ( "reserve-x18", - Forbidden { reason: "use `-Zfixed-x18` compiler flag instead", hard_error: false }, + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + InternalOnly { reason: "use `-Zfixed-x18` compiler flag instead", hard_error: false }, &[], ), // FEAT_SB @@ -492,7 +502,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rdseed", Stable, &[]), ( "retpoline-external-thunk", - Stability::Forbidden { + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { reason: "use `-Zretpoline-external-thunk` compiler flag instead", hard_error: false, }, @@ -500,7 +511,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ), ( "retpoline-indirect-branches", - Stability::Forbidden { + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { reason: "use `-Zretpoline` compiler flag instead", hard_error: false, }, @@ -508,7 +520,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ), ( "retpoline-indirect-calls", - Stability::Forbidden { + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { reason: "use `-Zretpoline` compiler flag instead", hard_error: false, }, @@ -521,7 +534,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sm4", Stable, &["avx2"]), ( "soft-float", - Stability::Forbidden { reason: "use a soft-float target instead", hard_error: false }, + // Pinned down by [`Target::abi_required_features`]. + Stability::InternalOnly { reason: "use a soft-float target instead", hard_error: false }, &[], ), ("sse", Stable, &[]), @@ -585,7 +599,8 @@ static POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("altivec", Unstable(sym::powerpc_target_feature), &[]), ( "hard-float", - Forbidden { reason: "unsupported ABI-configuration feature", hard_error: false }, + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, &[], ), ("msync", Unstable(sym::powerpc_target_feature), &[]), @@ -597,7 +612,12 @@ static POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), ("quadword-atomics", Unstable(sym::powerpc_target_feature), &[]), - ("spe", Forbidden { reason: "unsupported ABI-configuration feature", hard_error: false }, &[]), + ( + "spe", + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, + &[], + ), ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), // tidy-alphabetical-end ]; @@ -661,7 +681,8 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("f", CfgStableToggleUnstable(sym::riscv_target_feature), &["zicsr"]), ( "forced-atomics", - Stability::Forbidden { + // Not implied by any CPU model or other feature. + Stability::InternalOnly { reason: "unsound because it changes the ABI of atomic operations", hard_error: false, }, @@ -921,7 +942,8 @@ const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("miscellaneous-extensions-3", Stable, &[]), ("miscellaneous-extensions-4", Stable, &[]), ("nnp-assist", Stable, &["vector"]), - ("soft-float", Forbidden { reason: "unsupported ABI-configuration feature", hard_error: false }, &[]), + // Pinned down by [`Target::abi_required_features`]. + ("soft-float", InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, &[]), ("transactional-execution", Unstable(sym::s390x_target_feature), &[]), ("vector", Stable, &[]), ("vector-enhancements-1", Stable, &["vector"]), @@ -975,7 +997,8 @@ static AVR_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("spmx", Unstable(sym::avr_target_feature), &[]), ( "sram", - Forbidden { reason: "devices that have no SRAM are unsupported", hard_error: false }, + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "devices that have no SRAM are unsupported", hard_error: false }, &[], ), ("tinyencoding", Unstable(sym::avr_target_feature), &[]), @@ -990,7 +1013,11 @@ const XTENSA_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("interrupt", Unstable(sym::xtensa_target_feature), &["exception"]), ( "windowed", - Forbidden { reason: "windowed changes the Xtensa calling convention", hard_error: false }, + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { + reason: "windowed changes the Xtensa calling convention", + hard_error: false, + }, &["exception"], ), ("loop", Unstable(sym::xtensa_target_feature), &[]), @@ -1017,17 +1044,17 @@ const XTENSA_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ /// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator! pub fn all_rust_features() -> impl Iterator { std::iter::empty() - .chain(ARM_FEATURES.iter()) - .chain(AARCH64_FEATURES.iter()) - .chain(X86_FEATURES.iter()) - .chain(HEXAGON_FEATURES.iter()) - .chain(POWERPC_FEATURES.iter()) - .chain(MIPS_FEATURES.iter()) - .chain(NVPTX_FEATURES.iter()) - .chain(RISCV_FEATURES.iter()) - .chain(WASM_FEATURES.iter()) - .chain(BPF_FEATURES.iter()) - .chain(XTENSA_FEATURES.iter()) + .chain(ARM_FEATURES) + .chain(AARCH64_FEATURES) + .chain(X86_FEATURES) + .chain(HEXAGON_FEATURES) + .chain(POWERPC_FEATURES) + .chain(MIPS_FEATURES) + .chain(NVPTX_FEATURES) + .chain(RISCV_FEATURES) + .chain(WASM_FEATURES) + .chain(BPF_FEATURES) + .chain(XTENSA_FEATURES) .chain(CSKY_FEATURES) .chain(LOONGARCH_FEATURES) .chain(IBMZ_FEATURES) @@ -1152,6 +1179,16 @@ impl Target { } } + /// Computes a map mapping each Rust target feature to the features it implies. + pub fn rust_target_features_map( + &self, + ) -> FxHashMap<&'static str, (Stability, ImpliedFeatures)> { + self.rust_target_features() + .iter() + .map(|&(f, s, i)| (f, (s, i))) + .collect::>() + } + pub fn features_for_correct_fixed_length_vector_abi(&self) -> &'static [(u64, &'static str)] { match &self.arch { Arch::X86 | Arch::X86_64 => X86_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, @@ -1193,20 +1230,23 @@ impl Target { } } - // Note: the returned set includes `base_feature`. - pub fn implied_target_features<'a>(&self, base_feature: &'a str) -> FxHashSet<&'a str> { - let implied_features = - self.rust_target_features().iter().map(|(f, _, i)| (f, i)).collect::>(); - + /// Note: the returned set includes `base_feature`. + #[track_caller] + pub fn implied_target_features<'a>( + &self, + base_feature: &'a str, + target_features_map: &FxHashMap<&'static str, (Stability, ImpliedFeatures)>, + ) -> FxHashSet<&'a str> { // Implied target features have their own implied target features, so we traverse the // map until there are no more features to add. let mut features = FxHashSet::default(); let mut new_features = vec![base_feature]; while let Some(new_feature) = new_features.pop() { if features.insert(new_feature) { - if let Some(implied_features) = implied_features.get(&new_feature) { - new_features.extend(implied_features.iter().copied()) - } + let (_, implied_features) = target_features_map + .get(&new_feature) + .unwrap_or_else(|| panic!("encountered non-Rust target feature {new_feature}")); + new_features.extend(implied_features.iter().copied()); } } features @@ -1226,7 +1266,7 @@ impl Target { const NOTHING: FeatureConstraints = FeatureConstraints { required: &[], incompatible: &[] }; // Some architectures don't have a clean explicit ABI designation; instead, the ABI is // defined by target features. When that is the case, those target features must be - // "forbidden" in the list above to ensure that there is a consistent answer to the + // "internal-only" in the list above to ensure that there is a consistent answer to the // questions "which ABI is used". match &self.arch { Arch::X86 => { @@ -1301,9 +1341,9 @@ impl Target { // LLVM will use float registers when `fp-armv8` is available, e.g. for // calls to built-ins. The only way to ensure a consistent softfloat ABI // on aarch64 is to never enable `fp-armv8`, so we enforce that. - // In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is the - // feature we have to mark as incompatible. - FeatureConstraints { required: &[], incompatible: &["neon"] } + // In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is also + // marked as incompatible. + FeatureConstraints { required: &[], incompatible: &["neon", "fp-armv8"] } } None => { // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 81bee3dab088b..d2e8f136d7491 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -13,7 +13,7 @@ use rustc_errors::{ Applicability, Diag, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, pluralize, struct_span_code_err, }; -use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{Visitor, VisitorExt}; use rustc_hir::lang_items::LangItem; @@ -1764,7 +1764,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // If we didn't return early here, we would instead suggest `&&str::from("")`. return false; } else if let hir::ExprKind::Call(_, args) = expr.kind { - if let Some(pred) = self + // The `def_id` can point at a struct, which has no fn sig. + if matches!( + self.tcx.def_kind(*def_id), + DefKind::AssocFn | DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) + ) && let Some(pred) = self .tcx .clauses_of(*def_id) .instantiate_identity(self.tcx) @@ -1799,6 +1803,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { c @ ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) if let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) && let hir::ExprKind::MethodCall(_segment, rcvr, args, ..) = expr.kind + // The `def_id` can also point at the impl, which has no fn sig. + && matches!( + self.tcx.def_kind(*def_id), + DefKind::AssocFn | DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) + ) && let Some(pred) = self .tcx .clauses_of(*def_id) diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index e0cbc0890b761..e76d6b86d5e5c 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -364,8 +364,15 @@ impl Index for CanonicalVarValues { } } +#[derive_where(Default; I: Interner)] +pub struct CanonicalParamEnvCache( + pub HashMap>, +); + #[derive_where(Clone, Debug; I: Interner)] pub struct CanonicalParamEnvCacheEntry { + // Note: this `param_env` is the canonicalized form of the key for this entry in the enclosing + // `CanonicalParamEnvCache`. pub param_env: I::ParamEnv, pub variables: ThinVec, pub variable_lookup_table: HashMap, diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 9ca1ec19a1339..56a911bdb4b8b 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -18,8 +18,8 @@ use crate::solve::{ }; use crate::visit::{Flags, TypeVisitable}; use crate::{ - self as ty, BoundRegion, BoundVar, CanonicalParamEnvCacheEntry, DebruijnIndex, Region, - RegionKind, TraitRef, search_graph, + self as ty, BoundRegion, BoundVar, CanonicalParamEnvCache, DebruijnIndex, Region, RegionKind, + TraitRef, search_graph, }; #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_interner")] @@ -204,11 +204,9 @@ pub trait Interner: fn with_global_cache(self, f: impl FnOnce(&mut search_graph::GlobalCache) -> R) -> R; - fn canonical_param_env_cache_get_or_insert( + fn with_canonical_param_env_cache( self, - param_env: Self::ParamEnv, - f: impl FnOnce() -> CanonicalParamEnvCacheEntry, - from_entry: impl FnOnce(&CanonicalParamEnvCacheEntry) -> R, + f: impl FnOnce(&mut CanonicalParamEnvCache) -> R, ) -> R; /// Useful for testing. If a cache entry is replaced, this should diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index ae8458c199503..912623b73050e 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -591,6 +591,7 @@ pub const trait From: Sized { #[rustc_diagnostic_item = "from_fn"] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[lang = "from"] fn from(value: T) -> Self; } diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index 1e1053583a617..fb99e86139d7e 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -24,11 +24,13 @@ const fn contains_zero_byte(x: usize) -> bool { #[must_use] pub const fn memchr(x: u8, text: &[u8]) -> Option { // Fast path for small slices. - if text.len() < 2 * USIZE_BYTES { - return memchr_naive(x, text); + let result = + if text.len() < 2 * USIZE_BYTES { memchr_naive(x, text) } else { memchr_aligned(x, text) }; + if let Some(index) = result { + // SAFETY: Both implementations only return an index from within `text`. + unsafe { crate::hint::assert_unchecked(index < text.len()) }; } - - memchr_aligned(x, text) + result } #[inline] @@ -107,8 +109,18 @@ const fn memchr_aligned(x: u8, text: &[u8]) -> Option { } /// Returns the last index matching the byte `x` in `text`. +#[inline] #[must_use] pub fn memrchr(x: u8, text: &[u8]) -> Option { + let result = memrchr_aligned(x, text); + if let Some(index) = result { + // SAFETY: `memrchr_aligned` only returns the index of a matching byte in `text`. + unsafe { crate::hint::assert_unchecked(index < text.len()) }; + } + result +} + +fn memrchr_aligned(x: u8, text: &[u8]) -> Option { // Scan for a single byte value by reading two `usize` words at a time. // // Split `text` in three parts: diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 22b1ba7738af9..276aab67085b3 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -1814,6 +1814,17 @@ pub mod memchr { assert_eq!(None, memchr(b'a', b"xyz")); } + #[test] + fn each_alignment() { + let mut data = [1u8; 64]; + let needle = 2; + let pos = 40; + data[pos] = needle; + for start in 0..16 { + assert_eq!(Some(pos - start), memchr(needle, &data[start..])); + } + } + #[test] fn matches_one_reversed() { assert_eq!(Some(0), memrchr(b'a', b"a")); diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 9d204a150dd45..1644a2495d97e 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -36,11 +36,6 @@ cfg_select! { #[path = "hermit.rs"] mod imp; } - target_os = "l4re" => { - // L4Re is unix family but does not yet support unwinding. - #[path = "dummy.rs"] - mod imp; - } any( all(target_family = "windows", target_env = "gnu"), target_os = "psp", diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 4c5cd0e0c9e6a..3b8499758d90d 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -37,6 +37,7 @@ target_env = "sgx", target_os = "xous", target_os = "trusty", + target_os = "l4re", )) ))] mod tests; diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 1b069f2e77f6b..2a656e2f8c196 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -962,6 +962,10 @@ fn recursive_mkdir_slash() { } #[test] +#[cfg_attr( + target_os = "l4re", + ignore = "Path '.' in the file system root can not be resolved in L4Re" +)] fn recursive_mkdir_dot() { check!(fs::create_dir_all(Path::new("."))); } @@ -2117,6 +2121,7 @@ fn rename_directory() { } #[test] +#[cfg_attr(target_os = "l4re", ignore = "futimens")] fn test_file_times() { #[cfg(target_vendor = "apple")] use crate::os::darwin::fs::FileTimesExt; @@ -2145,7 +2150,8 @@ fn test_file_times() { target_os = "android", target_os = "redox", target_os = "espidf", - target_os = "horizon" + target_os = "horizon", + target_os = "l4re", )) ) )))] diff --git a/library/std/src/net/ip_addr.rs b/library/std/src/net/ip_addr.rs index 7262899b3bbbe..6bd78de910fec 100644 --- a/library/std/src/net/ip_addr.rs +++ b/library/std/src/net/ip_addr.rs @@ -1,5 +1,12 @@ // Tests for this module -#[cfg(all(test, not(any(target_os = "emscripten", all(target_os = "wasi", target_env = "p1")))))] +#[cfg(all( + test, + not(any( + target_os = "emscripten", + all(target_os = "wasi", target_env = "p1"), + target_os = "l4re" + )) +))] mod tests; #[stable(feature = "ip_addr", since = "1.7.0")] diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 2a8b0f8ca9aad..1b1096925dd4a 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -42,7 +42,7 @@ mod hostname; mod ip_addr; mod socket_addr; mod tcp; -#[cfg(test)] +#[cfg(all(test, not(target_os = "l4re")))] pub(crate) mod tests; mod udp; diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index cae14e34e73e7..2dab8c26f1f6b 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -1,5 +1,12 @@ // Tests for this module -#[cfg(all(test, not(any(target_os = "emscripten", all(target_os = "wasi", target_env = "p1")))))] +#[cfg(all( + test, + not(any( + target_os = "emscripten", + all(target_os = "wasi", target_env = "p1"), + target_os = "l4re" + )) +))] mod tests; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index d9090320bd5a6..4ba4c4e8caa4c 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -7,6 +7,7 @@ all(target_os = "wasi", target_env = "p1"), target_os = "xous", target_os = "trusty", + target_os = "l4re", )) ))] mod tests; diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index cd925b9bdfdf8..4aa77fc9c1fe9 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -6,6 +6,7 @@ target_env = "sgx", target_os = "xous", target_os = "trusty", + target_os = "l4re", )) ))] mod tests; diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs index 473d7ae3e2ae6..735f1cf8925fb 100644 --- a/library/std/src/os/fd/mod.rs +++ b/library/std/src/os/fd/mod.rs @@ -20,6 +20,7 @@ mod net; mod stdio; #[cfg(test)] +#[cfg(not(target_os = "l4re"))] mod tests; // Export the types and traits for the public API. diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 0d96958b6cca1..a0c96e2836fc5 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -16,7 +16,7 @@ use crate::io; use crate::os::hermit::io::OwnedFd; #[cfg(all(not(target_os = "hermit"), not(target_os = "motor")))] use crate::os::raw; -#[cfg(all(doc, not(any(target_arch = "wasm32", target_env = "sgx"))))] +#[cfg(all(doc, not(any(target_arch = "wasm32", target_env = "sgx", target_os = "l4re"))))] use crate::os::unix::io::AsFd; #[cfg(unix)] use crate::os::unix::io::OwnedFd; diff --git a/library/std/src/os/l4re/fs.rs b/library/std/src/os/l4re/fs.rs index 491e04a4d25cf..2dc899bcb5a6e 100644 --- a/library/std/src/os/l4re/fs.rs +++ b/library/std/src/os/l4re/fs.rs @@ -21,7 +21,7 @@ pub trait MetadataExt { /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the /// cross-Unix abstractions contained within the raw stat. /// - /// [`stat`]: struct@crate::os::linux::raw::stat + /// [`stat`]: struct@crate::os::l4re::raw::stat /// /// # Examples /// @@ -29,7 +29,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -50,7 +50,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -68,7 +68,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -86,7 +86,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -104,7 +104,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -122,7 +122,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -140,7 +140,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -158,7 +158,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -179,7 +179,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -197,7 +197,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -217,7 +217,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -235,7 +235,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -255,7 +255,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -273,7 +273,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -293,7 +293,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -311,7 +311,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -329,7 +329,7 @@ pub trait MetadataExt { #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; - /// use std::os::linux::fs::MetadataExt; + /// use std::os::l4re::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; @@ -345,7 +345,7 @@ pub trait MetadataExt { impl MetadataExt for Metadata { #[allow(deprecated)] fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) } + unsafe { &*(self.as_inner().as_inner() as *const _ as *const raw::stat) } } fn st_dev(&self) -> u64 { self.as_inner().as_inner().st_dev as u64 @@ -372,22 +372,22 @@ impl MetadataExt for Metadata { self.as_inner().as_inner().st_size as u64 } fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 + self.as_inner().as_inner().st_atim.tv_sec as i64 } fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 + self.as_inner().as_inner().st_atim.tv_nsec as i64 } fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 + self.as_inner().as_inner().st_mtim.tv_sec as i64 } fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 + self.as_inner().as_inner().st_mtim.tv_nsec as i64 } fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 + self.as_inner().as_inner().st_ctim.tv_sec as i64 } fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 + self.as_inner().as_inner().st_ctim.tv_nsec as i64 } fn st_blksize(&self) -> u64 { self.as_inner().as_inner().st_blksize as u64 diff --git a/library/std/src/os/l4re/raw.rs b/library/std/src/os/l4re/raw.rs index 8fb6e99ecfa1e..f41fff015cab6 100644 --- a/library/std/src/os/l4re/raw.rs +++ b/library/std/src/os/l4re/raw.rs @@ -10,355 +10,14 @@ )] #![allow(deprecated)] -use crate::os::raw::c_ulong; - #[stable(feature = "raw_ext", since = "1.1.0")] -pub type dev_t = u64; +pub type dev_t = libc::dev_t; #[stable(feature = "raw_ext", since = "1.1.0")] -pub type mode_t = u32; +pub type mode_t = libc::mode_t; #[stable(feature = "pthread_t", since = "1.8.0")] -pub type pthread_t = c_ulong; +pub type pthread_t = libc::pthread_t; #[doc(inline)] #[stable(feature = "raw_ext", since = "1.1.0")] -pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; - -#[cfg(any( - target_arch = "x86", - target_arch = "m68k", - target_arch = "csky", - target_arch = "powerpc", - target_arch = "sparc", - target_arch = "arm", - target_arch = "wasm32" -))] -mod arch { - use crate::os::raw::{c_long, c_short, c_uint}; - - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; - - #[repr(C)] - #[derive(Clone)] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad1: c_short, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __st_ino: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad2: c_uint, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - } -} - -#[cfg(target_arch = "mips")] -mod arch { - use crate::os::raw::{c_long, c_ulong}; - - #[cfg(target_env = "musl")] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = i64; - #[cfg(not(target_env = "musl"))] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; - #[cfg(target_env = "musl")] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; - #[cfg(not(target_env = "musl"))] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; - #[cfg(target_env = "musl")] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; - #[cfg(not(target_env = "musl"))] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; - - #[repr(C)] - #[derive(Clone)] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: c_ulong, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_pad1: [c_long; 3], - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: c_ulong, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_pad2: [c_long; 2], - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_pad5: [c_long; 14], - } -} - -#[cfg(target_arch = "hexagon")] -mod arch { - use crate::os::raw::{c_int, c_long, c_uint}; - - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = i64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = c_long; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = c_uint; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = i64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; - - #[repr(C)] - #[derive(Clone)] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad1: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad2: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad3: [c_int; 2], - } -} - -#[cfg(any( - target_arch = "mips64", - target_arch = "s390x", - target_arch = "sparc64", - target_arch = "riscv64", - target_arch = "riscv32" -))] -mod arch { - pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; -} - -#[cfg(target_arch = "aarch64")] -mod arch { - use crate::os::raw::{c_int, c_long}; - - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = i64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = i32; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u32; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = i64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = c_long; - - #[repr(C)] - #[derive(Clone)] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad1: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad2: c_int, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __unused: [c_int; 2], - } -} - -#[cfg(any(target_arch = "x86_64", target_arch = "powerpc64"))] -mod arch { - use crate::os::raw::{c_int, c_long}; - - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; - - #[repr(C)] - #[derive(Clone)] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad0: c_int, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __unused: [c_long; 3], - } -} +pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 90ad137dac178..aa604aabaffce 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -18,6 +18,7 @@ use crate::sys::{AsInner, AsInnerMut, FromInner}; use crate::{io, sys}; // Tests for this module +#[cfg(not(target_os = "l4re"))] #[cfg(test)] mod tests; diff --git a/library/std/src/os/unix/net/mod.rs b/library/std/src/os/unix/net/mod.rs index 137088dd832f7..92d2696a5ef43 100644 --- a/library/std/src/os/unix/net/mod.rs +++ b/library/std/src/os/unix/net/mod.rs @@ -10,7 +10,7 @@ mod ancillary; mod datagram; mod listener; mod stream; -#[cfg(all(test, not(target_os = "emscripten")))] +#[cfg(all(test, not(any(target_os = "emscripten", target_os = "l4re"))))] mod tests; #[cfg(any( target_os = "android", diff --git a/library/std/src/process.rs b/library/std/src/process.rs index c5ffbbc666e43..a398363cf4bf9 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -157,6 +157,7 @@ target_os = "xous", target_os = "trusty", target_os = "hermit", + target_os = "l4re", )) ))] mod tests; diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 68c62a861075f..9fe14b2e468a5 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -28,7 +28,11 @@ fn shell_cmd() -> Command { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn smoke() { @@ -53,7 +57,11 @@ fn smoke_failure() { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn exit_reported_right() { @@ -71,7 +79,11 @@ fn exit_reported_right() { #[test] #[cfg(unix)] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn signal_reported_right() { @@ -98,7 +110,11 @@ pub fn run_output(mut cmd: Command) -> String { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn stdout_works() { @@ -116,7 +132,11 @@ fn stdout_works() { #[test] #[cfg_attr(windows, ignore)] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn set_current_dir_works() { @@ -142,7 +162,11 @@ fn set_current_dir_works() { #[test] #[cfg_attr(windows, ignore)] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn stdin_works() { @@ -163,7 +187,11 @@ fn stdin_works() { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn child_stdout_read_buf() { @@ -197,7 +225,11 @@ fn child_stdout_read_buf() { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn test_process_status() { @@ -217,6 +249,7 @@ fn test_process_status() { } #[test] +#[cfg_attr(any(target_os = "l4re"), ignore = "no fork/exec available")] fn test_process_output_fail_to_start() { match Command::new("/no-binary-by-this-name-should-exist").output() { Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound), @@ -226,7 +259,11 @@ fn test_process_output_fail_to_start() { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn test_process_output_output() { @@ -244,7 +281,11 @@ fn test_process_output_output() { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn test_process_output_error() { @@ -262,7 +303,11 @@ fn test_process_output_error() { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn test_finish_once() { @@ -276,7 +321,11 @@ fn test_finish_once() { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn test_finish_twice() { @@ -291,7 +340,11 @@ fn test_finish_twice() { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn test_wait_with_output_once() { @@ -329,7 +382,11 @@ pub fn env_cmd() -> Command { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn test_override_env() { @@ -355,7 +412,11 @@ fn test_override_env() { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn test_add_to_env() { @@ -370,7 +431,11 @@ fn test_add_to_env() { #[test] #[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + any( + target_os = "vxworks", + all(target_vendor = "apple", not(target_os = "macos")), + target_os = "l4re" + ), ignore = "no shell available" )] fn test_capture_env_at_spawn() { @@ -654,6 +719,7 @@ fn run_canonical_bat_script() { } #[test] +#[cfg_attr(target_os = "l4re", ignore = "no shell available")] fn terminate_exited_process() { let mut cmd = if cfg!(target_os = "android") { let mut p = shell_cmd(); diff --git a/library/std/src/random.rs b/library/std/src/random.rs index ef561d1ed0c60..853756fcd32b6 100644 --- a/library/std/src/random.rs +++ b/library/std/src/random.rs @@ -103,7 +103,7 @@ use crate::sys::random as sys; /// Vita | `arc4random_buf` /// Hermit | `read_entropy` /// Horizon, Cygwin | `getrandom` -/// AIX, Hurd, L4Re, QNX | `/dev/urandom` +/// AIX, Hurd, QNX | `/dev/urandom` /// Redox | `/scheme/rand` /// RTEMS | [`arc4random_buf`](https://docs.rtems.org/branches/main/bsp-howto/getentropy.html) /// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 3caa41e16845d..d34621083406a 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -29,19 +29,20 @@ use libc::{ }; #[cfg(not(any( all(target_os = "linux", not(target_env = "musl")), - target_os = "l4re", target_os = "android", target_os = "hurd", + target_os = "l4re", )))] use libc::{ dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, }; -#[cfg(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "l4re", - target_os = "hurd" -))] +#[cfg(target_os = "l4re")] +use libc::{ + dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, lstat as lstat64, + off_t as off64_t, open as open64, stat as stat64, +}; +#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}; use crate::ffi::{CStr, OsStr, OsString}; @@ -272,6 +273,7 @@ cfg_select! { target_os = "nto", target_os = "qnx", target_os = "vxworks", + target_os = "l4re", ) => { pub use crate::sys::fs::common::Dir; } @@ -560,7 +562,8 @@ impl FileAttr { target_os = "nto", target_os = "qnx", target_os = "aix", - target_os = "wasi" + target_os = "wasi", + target_os = "l4re" )))] impl FileAttr { #[cfg(not(any( @@ -686,7 +689,7 @@ impl FileAttr { } } -#[cfg(any(target_os = "nto", target_os = "qnx", target_os = "wasi"))] +#[cfg(any(target_os = "nto", target_os = "qnx", target_os = "wasi", target_os = "l4re"))] impl FileAttr { pub fn modified(&self) -> io::Result { SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec.into()) @@ -1066,6 +1069,7 @@ impl DirEntry { target_os = "nto", target_os = "qnx", target_os = "vita", + target_os = "l4re", ))] pub fn file_type(&self) -> io::Result { self.metadata().map(|m| m.file_type()) @@ -1080,6 +1084,7 @@ impl DirEntry { target_os = "nto", target_os = "qnx", target_os = "vita", + target_os = "l4re", )))] pub fn file_type(&self) -> io::Result { match self.entry.d_type { @@ -1289,6 +1294,7 @@ impl File { target_os = "nto", target_os = "qnx", target_os = "hurd", + target_os = "l4re", ))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) @@ -1304,6 +1310,7 @@ impl File { target_os = "nto", target_os = "qnx", target_os = "hurd", + target_os = "l4re", target_vendor = "apple", )))] unsafe fn os_datasync(fd: c_int) -> c_int { @@ -1550,7 +1557,7 @@ impl File { pub fn set_times(&self, times: FileTimes) -> io::Result<()> { cfg_select! { - any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx") => { + any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx", target_os = "l4re") => { // Redox doesn't appear to support `UTIME_OMIT`. // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore // the same as for Redox. @@ -1940,6 +1947,7 @@ pub fn link(original: &CStr, link: &CStr) -> io::Result<()> { // Other misc platforms target_os = "horizon", target_os = "vita", + target_os = "l4re", target_env = "nto70", ) => { cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; @@ -2308,6 +2316,7 @@ pub use remove_dir_impl::remove_dir_all; target_os = "nto", target_os = "qnx", target_os = "vxworks", + target_os = "l4re", miri ))] mod remove_dir_impl { @@ -2323,6 +2332,7 @@ mod remove_dir_impl { target_os = "nto", target_os = "qnx", target_os = "vxworks", + target_os = "l4re", miri )))] mod remove_dir_impl { diff --git a/library/std/src/sys/io/error/unix.rs b/library/std/src/sys/io/error/unix.rs index 89647ff27ca8e..5c51c5705a7aa 100644 --- a/library/std/src/sys/io/error/unix.rs +++ b/library/std/src/sys/io/error/unix.rs @@ -201,7 +201,8 @@ pub fn error_string(errno: i32) -> String { target_os = "linux", target_os = "hurd", target_env = "newlib", - target_os = "cygwin" + target_os = "cygwin", + target_env = "uclibc", ), not(target_env = "ohos") ), diff --git a/library/std/src/sys/net/connection/mod.rs b/library/std/src/sys/net/connection/mod.rs index 84b53fd375c93..49a0f47c959d2 100644 --- a/library/std/src/sys/net/connection/mod.rs +++ b/library/std/src/sys/net/connection/mod.rs @@ -1,6 +1,6 @@ cfg_select! { any( - all(target_family = "unix", not(target_os = "l4re")), + target_family = "unix", target_os = "windows", target_os = "hermit", all(target_os = "wasi", any(target_env = "p2", target_env = "p3")), diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 769dc66af8ed1..b7840d2c054c3 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -1,4 +1,5 @@ #[cfg(test)] +#[cfg(not(target_os = "l4re"))] mod tests; use crate::ffi::{c_int, c_void}; diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 8fca169d93119..f58c7f5cb95bc 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -145,6 +145,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "horizon", target_os = "vxworks", target_os = "vita", + target_os = "l4re", // Unikraft's `signal` implementation is currently broken: // https://github.com/unikraft/lib-musl/issues/57 target_vendor = "unikraft", @@ -363,15 +364,24 @@ cfg_select! { _ => {} } -#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] -pub mod unsupported { - use crate::io; - - pub fn unsupported() -> io::Result { - Err(unsupported_err()) - } +#[cfg(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx", + target_os = "l4re", +))] +pub fn unsupported() -> crate::io::Result { + Err(unsupported_err()) +} - pub fn unsupported_err() -> io::Error { - io::Error::UNSUPPORTED_PLATFORM - } +#[cfg(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx", + target_os = "l4re", +))] +pub fn unsupported_err() -> crate::io::Error { + io::Error::UNSUPPORTED_PLATFORM } diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 3b363aa2d024c..daa53703994b0 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -30,7 +30,7 @@ cfg_select! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "nuttx")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "nuttx")), all(target_vendor = "fortanix", target_env = "sgx"), ) => { mod gcc; diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs index ee61175a278b0..f46870e0c4042 100644 --- a/library/std/src/sys/process/mod.rs +++ b/library/std/src/sys/process/mod.rs @@ -45,7 +45,8 @@ pub use imp::{ target_os = "espidf", target_os = "horizon", target_os = "vita", - target_os = "nuttx" + target_os = "nuttx", + target_os = "l4re" )) ), target_os = "windows", @@ -83,7 +84,8 @@ pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec< target_os = "espidf", target_os = "horizon", target_os = "vita", - target_os = "nuttx" + target_os = "nuttx", + target_os = "l4re" )) ), target_os = "windows", diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index 8215b196127ac..2e32770e90e77 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -12,7 +12,7 @@ use crate::path::Path; use crate::process::StdioPipes; use crate::sys::fd::FileDesc; use crate::sys::fs::File; -#[cfg(not(target_os = "fuchsia"))] +#[cfg(not(any(target_os = "fuchsia", target_os = "l4re")))] use crate::sys::fs::OpenOptions; use crate::sys::pipe::pipe; use crate::sys::process::env::{CommandEnv, CommandEnvs, CommandResolvedEnvs}; @@ -24,6 +24,9 @@ mod cstring_array; cfg_select! { target_os = "fuchsia" => { // fuchsia doesn't have /dev/null + }, + target_os = "l4re" => { + // l4re doesn't have /dev/null } target_os = "vxworks" => { const DEV_NULL: &CStr = c"/null"; @@ -119,9 +122,9 @@ pub enum ChildStdio { Explicit(c_int), Owned(FileDesc), - // On Fuchsia, null stdio is the default, so we simply don't specify - // any actions at the time of spawning. - #[cfg(target_os = "fuchsia")] + // On Fuchsia and L4Re, null stdio is the default, so we simply don't + // specify any actions at the time of spawning. + #[cfg(any(target_os = "fuchsia", target_os = "l4re"))] Null, } @@ -427,7 +430,7 @@ impl Stdio { Ok((ChildStdio::Owned(theirs), Some(ours))) } - #[cfg(not(target_os = "fuchsia"))] + #[cfg(not(any(target_os = "fuchsia", target_os = "l4re")))] Stdio::Null => { let mut opts = OpenOptions::new(); opts.read(readable); @@ -436,7 +439,7 @@ impl Stdio { Ok((ChildStdio::Owned(fd.into_inner()), None)) } - #[cfg(target_os = "fuchsia")] + #[cfg(any(target_os = "fuchsia", target_os = "l4re"))] Stdio::Null => Ok((ChildStdio::Null, None)), } } @@ -483,7 +486,7 @@ impl ChildStdio { ChildStdio::Explicit(fd) => Some(fd), ChildStdio::Owned(ref fd) => Some(fd.as_raw_fd()), - #[cfg(target_os = "fuchsia")] + #[cfg(any(target_os = "fuchsia", target_os = "l4re"))] ChildStdio::Null => None, } } diff --git a/library/std/src/sys/process/unix/common/tests.rs b/library/std/src/sys/process/unix/common/tests.rs index bc1d158b74861..eacb4d2d43122 100644 --- a/library/std/src/sys/process/unix/common/tests.rs +++ b/library/std/src/sys/process/unix/common/tests.rs @@ -19,6 +19,8 @@ macro_rules! t { // newly spawned process may just be raced in the macOS, so to prevent this // test from being flaky we ignore it on macOS. target_os = "macos", + // cat not available + target_os = "l4re", // When run under our current QEMU emulation test suite this test fails, // although the reason isn't very clear as to why. For now this test is // ignored there. @@ -84,6 +86,8 @@ fn test_process_mask() { any( // See test_process_mask target_os = "macos", + // cat not available + target_os = "l4re", target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64", @@ -116,6 +120,8 @@ fn test_process_group_posix_spawn() { any( // See test_process_mask target_os = "macos", + // cat not available + target_os = "l4re", target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64", @@ -154,6 +160,8 @@ fn test_process_group_no_posix_spawn() { any( // See test_process_mask target_os = "macos", + // cat not available + target_os = "l4re", target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64", @@ -192,6 +200,8 @@ fn test_setsid_posix_spawn() { any( // See test_process_mask target_os = "macos", + // cat not available + target_os = "l4re", target_arch = "arm", target_arch = "aarch64", target_arch = "riscv64", diff --git a/library/std/src/sys/process/unix/mod.rs b/library/std/src/sys/process/unix/mod.rs index 837761431e990..47baf5a1e92ed 100644 --- a/library/std/src/sys/process/unix/mod.rs +++ b/library/std/src/sys/process/unix/mod.rs @@ -1,4 +1,7 @@ -#[cfg_attr(any(target_os = "espidf", target_os = "horizon", target_os = "nuttx"), allow(unused))] +#[cfg_attr( + any(target_os = "espidf", target_os = "horizon", target_os = "nuttx", target_os = "l4re"), + allow(unused) +)] mod common; cfg_select! { @@ -10,7 +13,7 @@ cfg_select! { mod vxworks; use vxworks as imp; } - any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx") => { + any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx", target_os = "l4re") => { mod unsupported; use unsupported as imp; pub use unsupported::output; diff --git a/library/std/src/sys/process/unix/unix/tests.rs b/library/std/src/sys/process/unix/unix/tests.rs index 663ba61f966c9..9a029f16a3a20 100644 --- a/library/std/src/sys/process/unix/unix/tests.rs +++ b/library/std/src/sys/process/unix/unix/tests.rs @@ -51,7 +51,10 @@ fn exitstatus_display_tests() { #[test] #[cfg_attr(target_os = "emscripten", ignore)] -#[cfg_attr(any(target_os = "tvos", target_os = "watchos"), ignore = "fork is prohibited")] +#[cfg_attr( + any(target_os = "tvos", target_os = "watchos", target_os = "l4re"), + ignore = "fork is prohibited" +)] fn test_command_fork_no_unwind() { let got = catch_unwind(|| { let mut c = Command::new("echo"); diff --git a/library/std/src/sys/process/unix/unsupported.rs b/library/std/src/sys/process/unix/unsupported.rs index 17421d1e2e35d..2235ec1f1c3b6 100644 --- a/library/std/src/sys/process/unix/unsupported.rs +++ b/library/std/src/sys/process/unix/unsupported.rs @@ -4,7 +4,7 @@ use super::common::*; use crate::io; use crate::num::NonZero; use crate::process::StdioPipes; -use crate::sys::pal::unsupported::*; +use crate::sys::pal::{unsupported, unsupported_err}; //////////////////////////////////////////////////////////////////////////////// // Command diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index e5a66dc463c6b..5b0d19cc63eca 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -52,7 +52,6 @@ cfg_select! { any( target_os = "aix", target_os = "hurd", - target_os = "l4re", target_os = "nto", target_os = "qnx", ) => { @@ -107,6 +106,7 @@ cfg_select! { all(target_family = "wasm", target_os = "unknown"), target_os = "xous", target_os = "vexos", + target_os = "l4re", ) => { // FIXME: finally remove std support for wasm32-unknown-unknown // FIXME: add random data generation to xous @@ -123,6 +123,7 @@ cfg_select! { all(target_os = "wasi", not(target_env = "p1")), target_os = "xous", target_os = "vexos", + target_os = "l4re", )))] pub fn hashmap_random_keys() -> (u64, u64) { let mut buf = [0; 16]; diff --git a/library/std/src/thread/functions.rs b/library/std/src/thread/functions.rs index 21e7a2b2ed087..355a00c2a95ad 100644 --- a/library/std/src/thread/functions.rs +++ b/library/std/src/thread/functions.rs @@ -681,13 +681,10 @@ pub fn park_timeout(dur: Duration) { /// # Examples /// /// ``` -/// # #![allow(dead_code)] -/// use std::{io, thread}; +/// use std::thread; /// -/// fn main() -> io::Result<()> { -/// let count = thread::available_parallelism()?.get(); -/// assert!(count >= 1_usize); -/// Ok(()) +/// if let Ok(count) = thread::available_parallelism() { +/// assert!(count.get() >= 1_usize); /// } /// ``` #[doc(alias = "available_concurrency")] // Alias for a previous name we gave this API on unstable. diff --git a/library/std/tests/env.rs b/library/std/tests/env.rs index 9d624d5592ce7..758d0a069a831 100644 --- a/library/std/tests/env.rs +++ b/library/std/tests/env.rs @@ -4,7 +4,10 @@ use std::path::Path; mod common; #[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi", target_env = "sgx"), ignore)] +#[cfg_attr( + any(target_os = "emscripten", target_os = "wasi", target_env = "sgx", target_os = "l4re"), + ignore +)] fn test_self_exe_path() { let path = current_exe(); assert!(path.is_ok()); diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index 9643c3b7bdad8..c14db690224db 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -1,6 +1,10 @@ fn main() { - // No `Command` on Miri and emscripten - #[cfg(all(not(miri), any(unix, windows), not(target_os = "emscripten")))] + // No `Command` on Miri, emscripten or L4Re + #[cfg(all( + not(miri), + any(unix, windows), + not(any(target_os = "emscripten", target_os = "l4re")) + ))] { use std::io::{Read, pipe}; use std::{env, process}; diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 80e712a2388a1..b7a9a1077696b 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -7,7 +7,10 @@ mod common; #[test] // Process spawning not supported by Miri, Emscripten and wasi #[cfg_attr(any(miri, target_os = "emscripten", target_os = "wasi"), ignore)] -#[cfg_attr(any(target_os = "tvos", target_os = "watchos"), ignore = "fork is prohibited")] +#[cfg_attr( + any(target_os = "tvos", target_os = "watchos", target_os = "l4re"), + ignore = "fork is prohibited" +)] fn issue_15149() { // If we're the parent, copy our own binary to a new directory. let my_path = env::current_exe().unwrap(); diff --git a/library/std/tests/time.rs b/library/std/tests/time.rs index d6736e25ace18..6d8b4cbfd094f 100644 --- a/library/std/tests/time.rs +++ b/library/std/tests/time.rs @@ -181,6 +181,7 @@ fn system_time_elapsed() { } #[test] +#[cfg_attr(target_os = "l4re", ignore = "No wallclock time support in L4Re")] fn since_epoch() { let ts = SystemTime::now(); let a = ts.duration_since(UNIX_EPOCH + Duration::SECOND).unwrap(); diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 3725375a713dc..eba08aec4d109 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -19,7 +19,6 @@ cfg_select! { // Windows MSVC no extra unwinder support needed } any( - target_os = "l4re", target_os = "none", target_os = "espidf", target_os = "nuttx", @@ -31,6 +30,7 @@ cfg_select! { windows, target_os = "psp", target_os = "solid_asp3", + target_os = "l4re", all(target_vendor = "fortanix", target_env = "sgx"), all(target_os = "wasi", panic = "unwind"), target_os = "xous", diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 3c4815993b786..f3bc9840f8e45 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -20,6 +20,23 @@ use crate::core::config::TargetSelection; use crate::utils::build_stamp::{self, BuildStamp}; use crate::{CodegenBackendKind, Compiler, Mode, Subcommand, t}; +/// Allows individual check-step instances to keep track of whether they +/// represent `cargo check` or `cargo fix`, independently of [`Builder::kind`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum CheckKind { + Check, + Fix, +} + +impl CheckKind { + fn to_kind(self) -> Kind { + match self { + CheckKind::Check => Kind::Check, + CheckKind::Fix => Kind::Fix, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Std { /// Compiler that will check this std. @@ -225,11 +242,7 @@ impl Step for PrepareRustcRmetaSysroot { fn run(self, builder: &Builder<'_>) -> Self::Output { // Check rustc - let stamp = builder.ensure(Rustc::from_build_compiler( - self.build_compiler.clone(), - self.target, - vec![], - )); + let stamp = Rustc::check_rustc_for_preparing_sysroot(builder, &self); let build_compiler = self.build_compiler.build_compiler(); @@ -284,9 +297,12 @@ impl Step for PrepareStdRmetaSysroot { /// Checks rustc using `build_compiler`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustc { + check_kind: CheckKind, + /// Compiler that will check this rustc. - pub build_compiler: CompilerForCheck, - pub target: TargetSelection, + build_compiler: CompilerForCheck, + target: TargetSelection, + /// Whether to build only a subset of crates. /// /// This shouldn't be used from other steps; see the comment on [`compile::Rustc`]. @@ -296,17 +312,17 @@ pub struct Rustc { } impl Rustc { - pub fn new(builder: &Builder<'_>, target: TargetSelection, crates: Vec) -> Self { - let build_compiler = prepare_compiler_for_check(builder, target, Mode::Rustc); - Self::from_build_compiler(build_compiler, target, crates) - } - - fn from_build_compiler( - build_compiler: CompilerForCheck, - target: TargetSelection, - crates: Vec, - ) -> Self { - Self { build_compiler, target, crates } + fn check_rustc_for_preparing_sysroot( + builder: &Builder<'_>, + prepare: &PrepareRustcRmetaSysroot, + ) -> BuildStamp { + builder.ensure(Rustc { + // We specifically want `cargo check`, not the current bootstrap subcommand. + check_kind: CheckKind::Check, + build_compiler: prepare.build_compiler.clone(), + target: prepare.target, + crates: vec![], + }) } } @@ -323,8 +339,17 @@ impl CommandLineStep for Rustc { } fn make_run(run: RunConfig<'_>) { + let check_kind = match run.builder.kind { + Kind::Check => CheckKind::Check, + Kind::Fix => CheckKind::Fix, + kind => panic!("unexpected kind for `check::Rustc`: {kind:?}"), + }; + + let target = run.target; + let build_compiler = prepare_compiler_for_check(run.builder, target, Mode::Rustc); let crates = run.make_run_crates(Alias::Compiler); - run.builder.ensure(Rustc::new(run.builder, run.target, crates)); + + run.builder.ensure(Rustc { check_kind, build_compiler, target, crates }); } /// Check the compiler. @@ -344,7 +369,7 @@ impl CommandLineStep for Rustc { Mode::Rustc, SourceType::InTree, target, - Kind::Check, + self.check_kind.to_kind(), ); rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates); @@ -358,7 +383,7 @@ impl CommandLineStep for Rustc { } let _guard = builder.msg( - Kind::Check, + self.check_kind.to_kind(), format_args!("compiler artifacts{}", crate_description(&self.crates)), Mode::Rustc, self.build_compiler.build_compiler(), @@ -381,13 +406,11 @@ impl CommandLineStep for Rustc { } fn metadata(&self) -> Option { - let metadata = StepMetadata::check("rustc", self.target) + let mut metadata = StepMetadata::new("rustc", self.target, self.check_kind.to_kind()) .built_by(self.build_compiler.build_compiler()); - let metadata = if self.crates.is_empty() { - metadata - } else { - metadata.with_metadata(format!("({} crates)", self.crates.len())) - }; + if !self.crates.is_empty() { + metadata = metadata.with_metadata(format!("({} crates)", self.crates.len())); + } Some(metadata) } } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 652e797538223..315662b18f0a6 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2281,10 +2281,18 @@ impl CommandLineStep for Assemble { if builder.config.llvm_offload && !builder.config.dry_run() { debug!("`llvm_offload` requested"); + let rust_offload = builder.ensure(llvm::RustOffload { target: build_compiler.host }); let offload_install = builder.ensure(llvm::OmpOffload { target: build_compiler.host }); if let Some(_llvm_config) = builder.llvm_config(builder.config.host_target) { let target_libdir = builder.sysroot_target_libdir(target_compiler, target_compiler.host); + let rust_offload_dst_lib = target_libdir.join(rust_offload.rust_offload_filename()); + builder.copy_link( + &rust_offload.rust_offload_path(), + &rust_offload_dst_lib, + FileType::NativeLibrary, + ); + for p in offload_install.offload_paths() { let libname = p.file_name().unwrap(); let dst_lib = target_libdir.join(libname); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index d3276cfb5371b..2e14082310350 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -942,6 +942,96 @@ fn get_var(var_base: &str, host: &str, target: &str) -> Option { .or_else(|| env::var_os(var_base)) } +#[derive(Clone)] +pub struct BuiltRustOffload { + /// Path to the rust offload dylib + offload: PathBuf, +} + +impl BuiltRustOffload { + pub fn rust_offload_path(&self) -> PathBuf { + self.offload.clone() + } + + pub fn rust_offload_filename(&self) -> String { + self.offload.file_name().unwrap().to_str().unwrap().to_owned() + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct RustOffload { + pub target: TargetSelection, +} + +impl CommandLineStep for RustOffload { + type Output = BuiltRustOffload; + const IS_HOST: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("rust-offload") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(RustOffload { target: run.target }); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + if builder.config.dry_run() { + return BuiltRustOffload { + offload: builder.config.tempdir().join("rust-offload-dry-run"), + }; + } + + let target = self.target; + + let LlvmResult { host_llvm_config, llvm_cmake_dir } = builder.ensure(Llvm { target }); + + let out_dir = builder.rust_offload_out(target); + + let llvm_version_major = llvm::get_llvm_version_major(builder, &host_llvm_config); + let lib_ext = std::env::consts::DLL_EXTENSION; + let lib_rust_offload = format!("libRustOffload-{llvm_version_major}"); + let build_dir = out_dir.join(libdir(target)); + let dylib = build_dir.join(&lib_rust_offload).with_extension(lib_ext); + + let mut cfg = + cmake::Config::new(builder.src.join("compiler/rustc_llvm/llvm-wrapper/offload/")); + + // Logic copied from `configure_llvm` + // ThinLTO is only available when building with LLVM, enabling LLD is required. + // Apple's linker ld64 supports ThinLTO out of the box though, so don't use LLD on Darwin. + let mut ldflags = LdFlags::default(); + if builder.config.llvm_thin_lto && !target.contains("apple") { + ldflags.push_all("-fuse-ld=lld"); + } + + configure_cmake(builder, target, &mut cfg, true, ldflags, CcFlags::default(), &[]); + + let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) { + (false, _) => "Debug", + (true, false) => "Release", + (true, true) => "RelWithDebInfo", + }; + + cfg.out_dir(&out_dir) + .profile(profile) + .env("LLVM_CONFIG_REAL", &host_llvm_config) + .define("LLVM_DIR", llvm_cmake_dir); + + cfg.build(); + + if !dylib.exists() { + eprintln!( + "`{lib_rust_offload}` not found in `{}`. Either the build has failed or RustOffload was built with a wrong version of LLVM", + build_dir.display() + ); + exit!(1); + } + + BuiltRustOffload { offload: dylib } + } +} + #[derive(Clone)] pub struct BuiltOmpOffload { /// Path to the omp and offload dylibs. @@ -998,7 +1088,7 @@ impl CommandLineStep for OmpOffload { // Running cmake twice in the same folder is known to cause issues, like deleting existing // binaries. We therefore write our offload artifacts into it's own folder, instead of // using the llvm build dir. - let out_dir = builder.offload_out(target); + let out_dir = builder.omp_offload_out(target); let mut files = vec![]; let lib_ext = std::env::consts::DLL_EXTENSION; diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 603ef65854cf6..91fa2604df0c0 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -229,7 +229,7 @@ impl StepMetadata { Self::new(name, target, Kind::Run) } - fn new(name: &str, target: TargetSelection, kind: Kind) -> Self { + pub fn new(name: &str, target: TargetSelection, kind: Kind) -> Self { Self { name: name.to_string(), kind, target, built_by: None, stage: None, metadata: None } } @@ -805,6 +805,7 @@ impl<'a> Builder<'a> { tool::CargoMiri, llvm::Lld, llvm::Enzyme, + llvm::RustOffload, llvm::CrtBeginEnd, tool::RustdocGUITest, tool::OptimizedDist, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index dddb70b3fd468..57f50d981d1c4 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -3086,6 +3086,15 @@ mod snapshot { [run] rustc 0 -> miri 1 "); } + + #[test] + fn fix_compiler() { + let ctx = TestCtx::new(); + insta::assert_snapshot!(ctx.config("fix").path("compiler").render_steps(), @r" + [build] llvm + [fix] rustc 0 -> rustc 1 (74 crates) + "); + } } struct ExecutedSteps { diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 400d0715a4738..e4942a8ce669f 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -34,6 +34,7 @@ pub struct Finder { /// when the newly-bumped stage 0 compiler now knows about the formerly-missing targets. const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined + "aarch64-unknown-l4re-uclibc", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 7d119247b3bac..27a03b1616192 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -983,10 +983,14 @@ impl Build { self.out.join(&*target.triple).join("enzyme") } - fn offload_out(&self, target: TargetSelection) -> PathBuf { + fn omp_offload_out(&self, target: TargetSelection) -> PathBuf { self.out.join(&*target.triple).join("offload") } + fn rust_offload_out(&self, target: TargetSelection) -> PathBuf { + self.out.join(&*target.triple).join("rust-offload") + } + fn lld_out(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("lld") } diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index f4a9b5704a434..8cddd822c806e 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -227,7 +227,8 @@ pub fn use_host_linker(target: TargetSelection) -> bool { || target.contains("fortanix") || target.contains("fuchsia") || target.contains("bpf") - || target.contains("switch")) + || target.contains("switch") + || target.contains("l4re")) } pub fn target_supports_cranelift_backend(target: TargetSelection) -> bool { diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index 9b9cbe3862e39..8afda476ea68f 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -40,7 +40,9 @@ impl GitHubContext { fn get_run_type(&self) -> Option { match (self.event_name.as_str(), self.branch_ref.as_str()) { ("pull_request", _) => Some(RunType::PullRequest), - ("push", "refs/heads/try-perf") => Some(RunType::TryJob { job_patterns: None }), + ("push", "refs/heads/automation/bors/try-perf" | "refs/heads/try-perf") => { + Some(RunType::TryJob { job_patterns: None }) + } ("push", "refs/heads/automation/bors/try") => { let patterns = self.get_try_job_patterns(); let patterns = if !patterns.is_empty() { Some(patterns) } else { None }; diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index ca5890840581c..bedfa65ac894d 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -84,6 +84,7 @@ - [avr-none](platform-support/avr-none.md) - [\*-espidf](platform-support/esp-idf.md) - [\*-unknown-fuchsia](platform-support/fuchsia.md) + - [\*-unknown-l4re](platform-support/l4re.md) - [\*-unknown-trusty](platform-support/trusty.md) - [\*-kmc-solid_\*](platform-support/kmc-solid.md) - [csky-unknown-linux-gnuabiv2\*](platform-support/csky-unknown-linux-gnuabiv2.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 81e843263487c..c8ae02b091034 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -273,6 +273,7 @@ target | std | host | notes [`aarch64-unknown-helenos`](platform-support/helenos.md) | ✓ | | ARM64 HelenOS [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit [`aarch64-unknown-illumos`](platform-support/illumos.md) | ✓ | ✓ | ARM64 illumos +[`aarch64-unknown-l4re-uclibc`](platform-support/l4re.md) | ✓ | | ARM64 L4Re with uclibc `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) [`aarch64-unknown-linux-pauthtest`](platform-support/aarch64-unknown-linux-pauthtest.md) | ✓ | ✓ | ARM64 PAC ELF ABI [`aarch64-unknown-managarm-mlibc`](platform-support/managarm.md) | ? | | ARM64 Managarm @@ -459,7 +460,7 @@ target | std | host | notes [`x86_64-unknown-hermit`](platform-support/hermit.md) | ✓ | | x86_64 Hermit [`x86_64-unknown-helenos`](platform-support/helenos.md) | ✓ | | x86_64 (amd64) HelenOS [`x86_64-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 64-bit GNU/Hurd -`x86_64-unknown-l4re-uclibc` | ? | | +[`x86_64-unknown-l4re-uclibc`](platform-support/l4re.md) | ✓ | | x86_64 L4Re with uclibc [`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc [`x86_64-unknown-managarm-mlibc`](platform-support/managarm.md) | ? | | x86_64 Managarm [`x86_64-unknown-motor`](platform-support/motor.md) | ✓ | | x86_64 Motor OS diff --git a/src/doc/rustc/src/platform-support/l4re.md b/src/doc/rustc/src/platform-support/l4re.md new file mode 100644 index 0000000000000..56044319dc77a --- /dev/null +++ b/src/doc/rustc/src/platform-support/l4re.md @@ -0,0 +1,63 @@ +# `*-l4re-uclibc` + +**Tier: 3** + +[L4Re] is an open source, microkernel-based operating system and hypervisor. + +Target triplets available so far: + +- x86_64-unknown-l4re-uclibc +- aarch64-unknown-l4re-uclibc + +## Target maintainers + +- Marius Melzer ([@farao](https://github.com/farao)) + +## Requirements + +The L4Re targets are cross-compiled from a host environment, commonly Linux. +See [Getting Started] for options to set up L4Re. + +The L4Re sources can be found in the [Github Repos]. + +## Building an L4Re Rust Toolchain + +Configure one or several of the above L4Re targets and also add the host triple +in config.toml and build Rust as documented. Start off the toolchain by copying +`build/host/stage2/` to a self-chosen location. + +For each target, build an L4Re sysroot directory by running `make sysroot` in +the L4Re build directory. Copy the content of `sysroot/usr/lib/` into the +`self-contained` directory of the respective target in the Rust Toolchain +directory tree. + +Use rustup to install the L4Re Rust Toolchain locally: + +```sh +rustup toolchain link l4re +``` + +Now use the toolchain via a cargo (or directly a rustc) installed via `rustup`: + +```sh +cargo +l4re build --target +``` + +or + +```sh +rustc +l4re --target +``` + +## Run Rust Programs on L4Re + +You can run an L4Re application written in Rust just like any other externally +built (meaning not build with the L4Re build system) L4Re binary. A good option +is to build an L4Re image and add the application binary to the image and run it +via the ned script. The image can then be put on hardware or run on Qemu. + +See [l4re.org](https://l4re.org) for more information. + +[L4Re]: https://l4re.org +[Getting Started]: https://l4re.org/getting_started +[Github Repos]: https://github.com/L4Re diff --git a/src/doc/rustc/src/platform-support/netbsd.md b/src/doc/rustc/src/platform-support/netbsd.md index f7b57fff8a1f9..0d060f1de4cf5 100644 --- a/src/doc/rustc/src/platform-support/netbsd.md +++ b/src/doc/rustc/src/platform-support/netbsd.md @@ -24,10 +24,11 @@ are currently defined running NetBSD: | 3 | `sparc64-unknown-netbsd` | [Sun UltraSPARC systems](https://wiki.netbsd.org/ports/sparc64/) | All use the "native" `stdc++` library which goes along with the natively -supplied GNU C++ compiler for the given OS version. Many of the bootstraps -are built for NetBSD 9.x, although some exceptions exist (some -are built for NetBSD 8.x but also work on newer OS versions). -`x86_64-unknown-netbsd` is built for NetBSD 10.x to access a newer gcc. +supplied GNU C++ compiler for the given OS version. Most of the bootstraps +are built for NetBSD 9.x, although some exceptions exist (some are +built for newer NetBSD versions, due to target becoming usable first +with newer versions). `x86_64-unknown-netbsd` is built for NetBSD +10.x to access a newer gcc. ## Target Maintainers @@ -37,7 +38,7 @@ are built for NetBSD 8.x but also work on newer OS versions). Further contacts: -- [NetBSD/pkgsrc-wip's rust](https://github.com/NetBSD/pkgsrc-wip/blob/master/rust188/Makefile) maintainer (see MAINTAINER variable). This package is part of "pkgsrc work-in-progress" and is used for deployment and testing of new versions of rust. Note that we have the convention of having multiple rust versions active in pkgsrc-wip at any one time, so the version number is part of the directory name, and from time to time old versions are culled so this is not a fully "stable" link. +- [NetBSD/pkgsrc-wip's rust](https://github.com/NetBSD/pkgsrc-wip/blob/master/rust197/Makefile) maintainer (see MAINTAINER variable). This package is part of "pkgsrc work-in-progress" and is used for deployment and testing of new versions of rust. Note that we have the convention of having multiple rust versions active in pkgsrc-wip at any one time, so the version number is part of the directory name, and from time to time old versions are culled so this is not a fully "stable" link. - [NetBSD's pkgsrc lang/rust](https://github.com/NetBSD/pkgsrc/tree/trunk/lang/rust) for the "proper" package in pkgsrc. - [NetBSD's pkgsrc lang/rust-bin](https://github.com/NetBSD/pkgsrc/tree/trunk/lang/rust-bin) which re-uses the bootstrap kit as a binary distribution and therefore avoids the rather protracted native build time of rust itself diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index eb382f368905f..b19ed533d4953 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -1295,7 +1295,7 @@ fn format_integer_type(it: rustc_abi::IntegerType) -> String { pub(super) fn target(sess: &rustc_session::Session) -> Target { // Build a set of which features are enabled on this target let globally_enabled_features: FxHashSet<&str> = - sess.unstable_target_features.iter().map(|name| name.as_str()).collect(); + sess.internal_target_features.iter().map(|name| name.as_str()).collect(); // Build a map of target feature stability by feature name use rustc_target::target_features::Stability; diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 6953a27a39df8..a691cfc2df676 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -937,7 +937,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { target_feature: &str, ) -> InterpResult<'tcx, ()> { let this = self.eval_context_ref(); - if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) { + if !this.tcx.sess.internal_target_features.contains(&Symbol::intern(target_feature)) { throw_ub_format!( "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}" ); diff --git a/src/tools/miri/src/intrinsics/x86/mod.rs b/src/tools/miri/src/intrinsics/x86/mod.rs index d76d35cb722bc..25361a6435b0a 100644 --- a/src/tools/miri/src/intrinsics/x86/mod.rs +++ b/src/tools/miri/src/intrinsics/x86/mod.rs @@ -65,7 +65,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "sse2.pause" => { let [] = this.check_shim_sig_unadjusted(link_name, args)?; // Only exhibit the spin-loop hint behavior when SSE2 is enabled. - if this.tcx.sess.unstable_target_features.contains(&Symbol::intern("sse2")) { + if this.tcx.sess.internal_target_features.contains(&Symbol::intern("sse2")) { this.yield_active_thread(); } } diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index f476614992041..4ba10ce4612b4 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1203,14 +1203,14 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if attrs .target_features .iter() - .any(|feature| !ecx.tcx.sess.target_features.contains(&feature.name)) + .any(|feature| !ecx.tcx.sess.internal_target_features.contains(&feature.name)) { let unavailable = attrs .target_features .iter() .filter(|&feature| { feature.kind != TargetFeatureKind::Implied - && !ecx.tcx.sess.target_features.contains(&feature.name) + && !ecx.tcx.sess.internal_target_features.contains(&feature.name) }) .fold(String::new(), |mut s, feature| { if !s.is_empty() { diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index 0f9f68cfde787..49bced1dd5bd2 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -46,6 +46,9 @@ //@ revisions: aarch64_unknown_illumos //@ [aarch64_unknown_illumos] compile-flags: --target aarch64-unknown-illumos //@ [aarch64_unknown_illumos] needs-llvm-components: aarch64 +//@ revisions: aarch64_unknown_l4re_uclibc +//@ [aarch64_unknown_l4re_uclibc] compile-flags: --target aarch64-unknown-l4re-uclibc +//@ [aarch64_unknown_l4re_uclibc] needs-llvm-components: aarch64 //@ revisions: aarch64_unknown_linux_gnu //@ [aarch64_unknown_linux_gnu] compile-flags: --target aarch64-unknown-linux-gnu //@ [aarch64_unknown_linux_gnu] needs-llvm-components: aarch64 diff --git a/tests/mir-opt/const_promotion_extern_static.FOO-promoted[0].SimplifyCfg-pre-optimizations.after.mir b/tests/mir-opt/const_promotion_extern_static.FOO-promoted[0].SimplifyCfg-pre-optimizations.after.mir deleted file mode 100644 index 72cb64e275e36..0000000000000 --- a/tests/mir-opt/const_promotion_extern_static.FOO-promoted[0].SimplifyCfg-pre-optimizations.after.mir +++ /dev/null @@ -1,18 +0,0 @@ -// MIR for `FOO::promoted[0]` after SimplifyCfg-pre-optimizations - -const FOO::promoted[0]: &[&i32; 1] = { - let mut _0: &[&i32; 1]; - let mut _1: [&i32; 1]; - let mut _2: &i32; - let mut _3: *const i32; - - bb0: { - _3 = const {ALLOC0: *const i32}; - _2 = &(*_3); - _1 = [move _2]; - _0 = &_1; - return; - } -} - -ALLOC0 (extern static: X) diff --git a/tests/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff b/tests/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff deleted file mode 100644 index 0e4eed2c028d0..0000000000000 --- a/tests/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff +++ /dev/null @@ -1,44 +0,0 @@ -- // MIR for `FOO` before PromoteTemps -+ // MIR for `FOO` after PromoteTemps - - static mut FOO: *const &i32 = { - let mut _0: *const &i32; - let mut _1: &[&i32]; - let mut _2: &[&i32; 1]; - let _3: [&i32; 1]; - let mut _4: &i32; - let _5: *const i32; -+ let mut _6: &[&i32; 1]; - - bb0: { - StorageLive(_1); - StorageLive(_2); -- StorageLive(_3); -- StorageLive(_4); -- StorageLive(_5); -- _5 = const {ALLOC0: *const i32}; -- _4 = &(*_5); -- _3 = [move _4]; -- _2 = &_3; -+ _6 = const FOO::promoted[0]; -+ _2 = &(*_6); - _1 = move _2 as &[&i32] (PointerCoercion(Unsize, Implicit)); -- StorageDead(_4); - StorageDead(_2); - _0 = core::slice::::as_ptr(move _1) -> [return: bb1, unwind: bb2]; - } - - bb1: { -- StorageDead(_5); -- StorageDead(_3); - StorageDead(_1); - return; - } - - bb2 (cleanup): { - resume; - } - } -- -- ALLOC0 (extern static: X) - diff --git a/tests/mir-opt/const_promotion_extern_static.rs b/tests/mir-opt/const_promotion_extern_static.rs index f16a53270a97d..ec9368094a752 100644 --- a/tests/mir-opt/const_promotion_extern_static.rs +++ b/tests/mir-opt/const_promotion_extern_static.rs @@ -1,18 +1,11 @@ //@ skip-filecheck //@ ignore-endian-big -extern "C" { - static X: i32; -} static Y: i32 = 42; // EMIT_MIR const_promotion_extern_static.BAR.PromoteTemps.diff // EMIT_MIR const_promotion_extern_static.BAR-promoted[0].SimplifyCfg-pre-optimizations.after.mir static mut BAR: *const &i32 = [&Y].as_ptr(); -// EMIT_MIR const_promotion_extern_static.FOO.PromoteTemps.diff -// EMIT_MIR const_promotion_extern_static.FOO-promoted[0].SimplifyCfg-pre-optimizations.after.mir -static mut FOO: *const &i32 = [unsafe { &X }].as_ptr(); - // EMIT_MIR const_promotion_extern_static.BOP.built.after.mir static BOP: &i32 = &13; diff --git a/tests/pretty/delegation/self-mapping-output.pp b/tests/pretty/delegation/self-mapping-output.pp index 84e98d6e97b06..5bce43315e1d0 100644 --- a/tests/pretty/delegation/self-mapping-output.pp +++ b/tests/pretty/delegation/self-mapping-output.pp @@ -24,7 +24,7 @@ struct W(S); impl Trait for W { #[attr = Inline(Hint)] - fn method(self: _) -> _ { Self { 0: Trait::method(self.0) } } + fn method(self: _) -> _ { from(Self { 0: Trait::method(self.0) }) } #[attr = Inline(Hint)] fn r#static() -> _ { Trait::r#static() } //~^ WARN: function cannot return without recursing [unconditional_recursion] @@ -34,7 +34,7 @@ impl W { #[attr = Inline(Hint)] - fn method(self: _) -> _ { Self { 0: Trait::method(self.0) } } + fn method(self: _) -> _ { from(Self { 0: Trait::method(self.0) }) } #[attr = Inline(Hint)] fn r#static() -> _ { Trait::r#static() } #[attr = Inline(Hint)] diff --git a/tests/run-make/msvc-localized-linker-output/fake-linker.rs b/tests/run-make/msvc-localized-linker-output/fake-linker.rs new file mode 100644 index 0000000000000..2cbb68c4518bf --- /dev/null +++ b/tests/run-make/msvc-localized-linker-output/fake-linker.rs @@ -0,0 +1,22 @@ +fn main() { + // Simulate a localized (e.g. Japanese) `link.exe`, as printed when the + // English language pack is not installed and `VSLANG=1033` has no effect. + // This is "Creating library foo.dll.lib and object foo.dll.exp" in Japanese. + println!("ライブラリ foo.dll.lib とオブジェクト foo.dll.exp を作成中"); + // A file name containing an `LNK####`-looking fragment must not be + // mistaken for a diagnostic, which is why the matcher requires the + // structured `LINK : warning LNK####:` form. + println!("LNK2001.lib: progress message, not a diagnostic"); + for arg in std::env::args() { + if arg == "run_make_lnk" { + // Real diagnostics are structured as `LINK : warning LNK####:`. + println!("LINK : warning LNK2001: unresolved external symbol foo"); + // The one code-bearing informational line has no `LINK : ` prefix + // and keeps the exception that classifies it as `linker_info`. + println!( + "LNK6004: 'foo.exe' not found or not built by the last incremental link; \ + performing full link" + ); + } + } +} diff --git a/tests/run-make/msvc-localized-linker-output/main.rs b/tests/run-make/msvc-localized-linker-output/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make/msvc-localized-linker-output/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/msvc-localized-linker-output/rmake.rs b/tests/run-make/msvc-localized-linker-output/rmake.rs new file mode 100644 index 0000000000000..3d0e4c0d7b61d --- /dev/null +++ b/tests/run-make/msvc-localized-linker-output/rmake.rs @@ -0,0 +1,67 @@ +//@ only-msvc +//@ ignore-cross-compile (need to run the fake link.exe on the host) + +//! Tests that localized (non-English) MSVC `link.exe` progress messages are +//! classified as `linker_info`, not `linker_messages`. +//! +//! `link.exe` is hardcoded by rustc to run with `VSLANG=1033`, which only works +//! when an English language pack is installed. Without it, messages like +//! "Creating library ..." are printed in another language, and the English +//! string matching that used to detect them fails. Since all real diagnostics +//! carry a locale-independent `LNK####` code, printed in the structured +//! `LINK : warning LNK####:` form, any line without one is informational, no +//! matter the language it was printed in. + +use run_make_support::{bare_rustc, rustc, target}; + +fn main() { + // rustc prepends the sysroot's tools bin directory to the linker's `PATH`, + // which bare names like `link.exe` are resolved against. Put the fake + // `link.exe` there so it wins over the real linker; `-L` below keeps std + // available from the real sysroot. + let fake_sysroot = std::env::current_dir().unwrap().join("fake-sysroot"); + let tools_bin = fake_sysroot.join(format!("lib/rustlib/{}/bin", target())); + std::fs::create_dir_all(&tools_bin).unwrap(); + rustc().arg("fake-linker.rs").output(tools_bin.join("link.exe")).run(); + + let real_libdir = rustc().print("target-libdir").run().stdout_utf8(); + let real_libdir = real_libdir.trim(); + + let fake_link = |extra: &[&str]| { + let mut r = bare_rustc(); + r.input("main.rs") + .output("main") + .arg(format!("--sysroot={}", fake_sysroot.display())) + .arg(format!("-L{real_libdir}")) + // Matched by name against the linker's `PATH`, so the fake in the + // tools bin directory is used instead of the real VS linker. + .arg("-Clinker=link.exe") + // Overrides `rust.lld=true` on CI. + .arg("-Clinker-flavor=msvc"); + for a in extra { + r.arg(a); + } + r + }; + + // The localized progress line must not warn by default. + fake_link(&[]) + .run() + .assert_stderr_not_contains("linker stdout") + .assert_stderr_not_contains("ライブラリ foo.dll.lib とオブジェクト foo.dll.exp を作成中"); + + // It is still visible through `linker_info`, and must not be misclassified + // as `linker_messages`. + fake_link(&["-Wlinker_info", "-Dlinker_messages"]) // Fail if the message is misclassified. + .run() + .assert_stderr_contains("ライブラリ foo.dll.lib とオブジェクト foo.dll.exp を作成中"); + + // Real diagnostics keep their `LNK####` code and still warn. + fake_link(&["-Clink-arg=run_make_lnk"]) + .run() + .assert_stderr_contains( + "warning: linker stdout: LINK : warning LNK2001: unresolved external symbol foo", + ) + // The informational LNK6004 line stays hidden. + .assert_stderr_not_contains("LNK6004"); +} diff --git a/tests/ui/array-slice-vec/vec-mut-iter-borrow.stderr b/tests/ui/array-slice-vec/vec-mut-iter-borrow.stderr index d9343140fb1dc..7d32670effe3f 100644 --- a/tests/ui/array-slice-vec/vec-mut-iter-borrow.stderr +++ b/tests/ui/array-slice-vec/vec-mut-iter-borrow.stderr @@ -1,13 +1,18 @@ error[E0499]: cannot borrow `xs` as mutable more than once at a time --> $DIR/vec-mut-iter-borrow.rs:5:9 | -LL | for x in &mut xs { - | ------- - | | - | first mutable borrow occurs here - | first borrow later used here -LL | xs.push(1) - | ^^ second mutable borrow occurs here +LL | for x in &mut xs { + | - ------- + | | | + | | first mutable borrow occurs here + | _____| first borrow later used here + | | +LL | | xs.push(1) + | | ^^ second mutable borrow occurs here +LL | | } + | |_____- this for loop borrows `xs` mutably, preventing mutation within its body + | + = help: consider using an index-based loop instead, or collecting modifications into a separate collection error: aborting due to 1 previous error diff --git a/tests/ui/asm/naked-functions-ffi.rs b/tests/ui/asm/naked-functions/ffi.rs similarity index 100% rename from tests/ui/asm/naked-functions-ffi.rs rename to tests/ui/asm/naked-functions/ffi.rs diff --git a/tests/ui/asm/naked-functions-ffi.stderr b/tests/ui/asm/naked-functions/ffi.stderr similarity index 90% rename from tests/ui/asm/naked-functions-ffi.stderr rename to tests/ui/asm/naked-functions/ffi.stderr index f7893a3b8de98..63c0f263e45e0 100644 --- a/tests/ui/asm/naked-functions-ffi.stderr +++ b/tests/ui/asm/naked-functions/ffi.stderr @@ -1,5 +1,5 @@ warning: `extern` fn uses type `char`, which is not FFI-safe - --> $DIR/naked-functions-ffi.rs:8:28 + --> $DIR/ffi.rs:8:28 | LL | pub extern "C" fn naked(p: char) -> u128 { | ^^^^ not FFI-safe diff --git a/tests/ui/asm/naked-functions-inline.rs b/tests/ui/asm/naked-functions/inline.rs similarity index 100% rename from tests/ui/asm/naked-functions-inline.rs rename to tests/ui/asm/naked-functions/inline.rs diff --git a/tests/ui/asm/naked-functions-inline.stderr b/tests/ui/asm/naked-functions/inline.stderr similarity index 87% rename from tests/ui/asm/naked-functions-inline.stderr rename to tests/ui/asm/naked-functions/inline.stderr index 68648be72328e..fa44f92040b4a 100644 --- a/tests/ui/asm/naked-functions-inline.stderr +++ b/tests/ui/asm/naked-functions/inline.stderr @@ -1,5 +1,5 @@ error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:13:3 + --> $DIR/inline.rs:13:3 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here @@ -7,7 +7,7 @@ LL | #[inline] | ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:20:3 + --> $DIR/inline.rs:20:3 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here @@ -15,7 +15,7 @@ LL | #[inline(always)] | ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:27:3 + --> $DIR/inline.rs:27:3 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here @@ -23,7 +23,7 @@ LL | #[inline(never)] | ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:34:18 + --> $DIR/inline.rs:34:18 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here diff --git a/tests/ui/asm/naked-functions-instruction-set.rs b/tests/ui/asm/naked-functions/instruction-set.rs similarity index 100% rename from tests/ui/asm/naked-functions-instruction-set.rs rename to tests/ui/asm/naked-functions/instruction-set.rs diff --git a/tests/ui/asm/naked-invalid-attr.rs b/tests/ui/asm/naked-functions/invalid-attr.rs similarity index 100% rename from tests/ui/asm/naked-invalid-attr.rs rename to tests/ui/asm/naked-functions/invalid-attr.rs diff --git a/tests/ui/asm/naked-invalid-attr.stderr b/tests/ui/asm/naked-functions/invalid-attr.stderr similarity index 84% rename from tests/ui/asm/naked-invalid-attr.stderr rename to tests/ui/asm/naked-functions/invalid-attr.stderr index 0b55dbe0dbcf0..aa1d2fd07d38f 100644 --- a/tests/ui/asm/naked-invalid-attr.stderr +++ b/tests/ui/asm/naked-functions/invalid-attr.stderr @@ -1,11 +1,11 @@ error[E0433]: cannot find module or crate `a` in the crate root - --> $DIR/naked-invalid-attr.rs:57:5 + --> $DIR/invalid-attr.rs:57:5 | LL | #[::a] | ^ use of unresolved module or unlinked crate `a` error: the `naked` attribute cannot be used on crates - --> $DIR/naked-invalid-attr.rs:5:11 + --> $DIR/invalid-attr.rs:5:11 | LL | #![unsafe(naked)] | ^^^^^ @@ -13,7 +13,7 @@ LL | #![unsafe(naked)] = help: the `naked` attribute can only be applied to functions error: the `naked` attribute cannot be used on foreign functions - --> $DIR/naked-invalid-attr.rs:10:14 + --> $DIR/invalid-attr.rs:10:14 | LL | #[unsafe(naked)] | ^^^^^ @@ -21,7 +21,7 @@ LL | #[unsafe(naked)] = help: the `naked` attribute can only be applied to functions with a body error: the `naked` attribute cannot be used on structs - --> $DIR/naked-invalid-attr.rs:14:10 + --> $DIR/invalid-attr.rs:14:10 | LL | #[unsafe(naked)] | ^^^^^ @@ -29,7 +29,7 @@ LL | #[unsafe(naked)] = help: the `naked` attribute can only be applied to functions error: the `naked` attribute cannot be used on struct fields - --> $DIR/naked-invalid-attr.rs:17:14 + --> $DIR/invalid-attr.rs:17:14 | LL | #[unsafe(naked)] | ^^^^^ @@ -37,7 +37,7 @@ LL | #[unsafe(naked)] = help: the `naked` attribute can only be applied to functions error: the `naked` attribute cannot be used on required trait methods - --> $DIR/naked-invalid-attr.rs:23:14 + --> $DIR/invalid-attr.rs:23:14 | LL | #[unsafe(naked)] | ^^^^^ @@ -45,7 +45,7 @@ LL | #[unsafe(naked)] = help: the `naked` attribute can only be applied to functions with a body error: the `naked` attribute cannot be used on closures - --> $DIR/naked-invalid-attr.rs:52:14 + --> $DIR/invalid-attr.rs:52:14 | LL | #[unsafe(naked)] | ^^^^^ @@ -53,7 +53,7 @@ LL | #[unsafe(naked)] = help: the `naked` attribute can be applied to functions and methods error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-invalid-attr.rs:57:3 + --> $DIR/invalid-attr.rs:57:3 | LL | #[::a] | ^^^ the `::a` attribute is incompatible with `#[unsafe(naked)]` diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.rs b/tests/ui/asm/naked-functions/invalid-repr-attr.rs similarity index 100% rename from tests/ui/asm/naked-with-invalid-repr-attr.rs rename to tests/ui/asm/naked-functions/invalid-repr-attr.rs diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.stderr b/tests/ui/asm/naked-functions/invalid-repr-attr.stderr similarity index 79% rename from tests/ui/asm/naked-with-invalid-repr-attr.stderr rename to tests/ui/asm/naked-functions/invalid-repr-attr.stderr index 7f12510b8aad6..5b9482e3297c5 100644 --- a/tests/ui/asm/naked-with-invalid-repr-attr.stderr +++ b/tests/ui/asm/naked-functions/invalid-repr-attr.stderr @@ -1,5 +1,5 @@ error: the `repr(C)` attribute cannot be used on functions - --> $DIR/naked-with-invalid-repr-attr.rs:10:3 + --> $DIR/invalid-repr-attr.rs:10:3 | LL | #[repr(C)] | ^^^^^^^ @@ -7,7 +7,7 @@ LL | #[repr(C)] = help: the `repr(C)` attribute can only be applied to data types error: the `repr(transparent)` attribute cannot be used on functions - --> $DIR/naked-with-invalid-repr-attr.rs:17:3 + --> $DIR/invalid-repr-attr.rs:17:3 | LL | #[repr(transparent)] | ^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | #[repr(transparent)] = help: the `repr(transparent)` attribute can only be applied to data types error: the `repr(C)` attribute cannot be used on functions - --> $DIR/naked-with-invalid-repr-attr.rs:24:3 + --> $DIR/invalid-repr-attr.rs:24:3 | LL | #[repr(C)] | ^^^^^^^ @@ -23,7 +23,7 @@ LL | #[repr(C)] = help: the `repr(C)` attribute can only be applied to data types error: the `repr(C)` attribute cannot be used on functions - --> $DIR/naked-with-invalid-repr-attr.rs:33:3 + --> $DIR/invalid-repr-attr.rs:33:3 | LL | #[repr(C, packed)] | ^^^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | #[repr(C, packed)] = help: the `repr(C)` attribute can only be applied to data types error: the `repr(packed)` attribute cannot be used on functions - --> $DIR/naked-with-invalid-repr-attr.rs:33:3 + --> $DIR/invalid-repr-attr.rs:33:3 | LL | #[repr(C, packed)] | ^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | #[repr(C, packed)] = help: the `repr(packed)` attribute can only be applied to data types error: the `repr(u8)` attribute cannot be used on functions - --> $DIR/naked-with-invalid-repr-attr.rs:41:3 + --> $DIR/invalid-repr-attr.rs:41:3 | LL | #[repr(u8)] | ^^^^^^^^ diff --git a/tests/ui/asm/naked-asm-mono-sym-fn.rs b/tests/ui/asm/naked-functions/mono-sym-fn.rs similarity index 100% rename from tests/ui/asm/naked-asm-mono-sym-fn.rs rename to tests/ui/asm/naked-functions/mono-sym-fn.rs diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions/naked-functions.rs similarity index 100% rename from tests/ui/asm/naked-functions.rs rename to tests/ui/asm/naked-functions/naked-functions.rs diff --git a/tests/ui/asm/naked-functions.stderr b/tests/ui/asm/naked-functions/naked-functions.stderr similarity index 100% rename from tests/ui/asm/naked-functions.stderr rename to tests/ui/asm/naked-functions/naked-functions.stderr diff --git a/tests/ui/asm/naked-functions-rustic-abi.rs b/tests/ui/asm/naked-functions/rustic-abi.rs similarity index 100% rename from tests/ui/asm/naked-functions-rustic-abi.rs rename to tests/ui/asm/naked-functions/rustic-abi.rs diff --git a/tests/ui/asm/naked-function-shim.rs b/tests/ui/asm/naked-functions/shim.rs similarity index 100% rename from tests/ui/asm/naked-function-shim.rs rename to tests/ui/asm/naked-functions/shim.rs diff --git a/tests/ui/asm/naked-functions-target-feature.rs b/tests/ui/asm/naked-functions/target-feature.rs similarity index 100% rename from tests/ui/asm/naked-functions-target-feature.rs rename to tests/ui/asm/naked-functions/target-feature.rs diff --git a/tests/ui/asm/naked-functions-testattrs.rs b/tests/ui/asm/naked-functions/testattrs.rs similarity index 100% rename from tests/ui/asm/naked-functions-testattrs.rs rename to tests/ui/asm/naked-functions/testattrs.rs diff --git a/tests/ui/asm/naked-functions-testattrs.stderr b/tests/ui/asm/naked-functions/testattrs.stderr similarity index 85% rename from tests/ui/asm/naked-functions-testattrs.stderr rename to tests/ui/asm/naked-functions/testattrs.stderr index ad2041ec118b9..5039a202efb74 100644 --- a/tests/ui/asm/naked-functions-testattrs.stderr +++ b/tests/ui/asm/naked-functions/testattrs.stderr @@ -1,5 +1,5 @@ error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:11:1 + --> $DIR/testattrs.rs:11:1 | LL | #[test] | ------- function marked with testing attribute here @@ -7,7 +7,7 @@ LL | #[unsafe(naked)] | ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:19:1 + --> $DIR/testattrs.rs:19:1 | LL | #[test] | ------- function marked with testing attribute here @@ -15,7 +15,7 @@ LL | #[unsafe(naked)] | ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:27:1 + --> $DIR/testattrs.rs:27:1 | LL | #[test] | ------- function marked with testing attribute here @@ -23,7 +23,7 @@ LL | #[unsafe(naked)] | ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:34:1 + --> $DIR/testattrs.rs:34:1 | LL | #[bench] | -------- function marked with testing attribute here diff --git a/tests/ui/asm/naked-functions-unused.aarch64.stderr b/tests/ui/asm/naked-functions/unused.aarch64.stderr similarity index 83% rename from tests/ui/asm/naked-functions-unused.aarch64.stderr rename to tests/ui/asm/naked-functions/unused.aarch64.stderr index bfb2923b0b8d6..366d338d15b48 100644 --- a/tests/ui/asm/naked-functions-unused.aarch64.stderr +++ b/tests/ui/asm/naked-functions/unused.aarch64.stderr @@ -1,66 +1,66 @@ error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:16:32 + --> $DIR/unused.rs:23:32 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: the lint level is defined here - --> $DIR/naked-functions-unused.rs:5:9 + --> $DIR/unused.rs:11:9 | LL | #![deny(unused)] | ^^^^^^ = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:16:42 + --> $DIR/unused.rs:23:42 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:27:38 + --> $DIR/unused.rs:34:38 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:27:48 + --> $DIR/unused.rs:34:48 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:35:41 + --> $DIR/unused.rs:42:41 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:35:51 + --> $DIR/unused.rs:42:51 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:45:40 + --> $DIR/unused.rs:52:40 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:45:50 + --> $DIR/unused.rs:52:50 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:53:43 + --> $DIR/unused.rs:60:43 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:53:53 + --> $DIR/unused.rs:60:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` diff --git a/tests/ui/asm/naked-functions-unused.rs b/tests/ui/asm/naked-functions/unused.rs similarity index 86% rename from tests/ui/asm/naked-functions-unused.rs rename to tests/ui/asm/naked-functions/unused.rs index 945ab1a40ad0c..51e3e90c1a72e 100644 --- a/tests/ui/asm/naked-functions-unused.rs +++ b/tests/ui/asm/naked-functions/unused.rs @@ -1,9 +1,16 @@ +//@ add-minicore //@ revisions: x86_64 aarch64 -//@ needs-asm-support -//@[x86_64] only-x86_64 -//@[aarch64] only-aarch64 -#![deny(unused)] +//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86_64] needs-llvm-components: x86 +//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu +//@[aarch64] needs-llvm-components: aarch64 +//@ ignore-backends: gcc #![crate_type = "lib"] +#![feature(no_core)] +#![no_core] +#![deny(unused)] + +extern crate minicore; pub trait Trait { extern "C" fn trait_associated(a: usize, b: usize) -> usize; @@ -11,7 +18,7 @@ pub trait Trait { } pub mod normal { - use std::arch::asm; + use minicore::asm; pub extern "C" fn function(a: usize, b: usize) -> usize { //~^ ERROR unused variable: `a` @@ -61,7 +68,7 @@ pub mod normal { } pub mod naked { - use std::arch::naked_asm; + use minicore::naked_asm; #[unsafe(naked)] pub extern "C" fn function(a: usize, b: usize) -> usize { diff --git a/tests/ui/asm/naked-functions-unused.x86_64.stderr b/tests/ui/asm/naked-functions/unused.x86_64.stderr similarity index 83% rename from tests/ui/asm/naked-functions-unused.x86_64.stderr rename to tests/ui/asm/naked-functions/unused.x86_64.stderr index bfb2923b0b8d6..366d338d15b48 100644 --- a/tests/ui/asm/naked-functions-unused.x86_64.stderr +++ b/tests/ui/asm/naked-functions/unused.x86_64.stderr @@ -1,66 +1,66 @@ error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:16:32 + --> $DIR/unused.rs:23:32 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: the lint level is defined here - --> $DIR/naked-functions-unused.rs:5:9 + --> $DIR/unused.rs:11:9 | LL | #![deny(unused)] | ^^^^^^ = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:16:42 + --> $DIR/unused.rs:23:42 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:27:38 + --> $DIR/unused.rs:34:38 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:27:48 + --> $DIR/unused.rs:34:48 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:35:41 + --> $DIR/unused.rs:42:41 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:35:51 + --> $DIR/unused.rs:42:51 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:45:40 + --> $DIR/unused.rs:52:40 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:45:50 + --> $DIR/unused.rs:52:50 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:53:43 + --> $DIR/unused.rs:60:43 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:53:53 + --> $DIR/unused.rs:60:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` diff --git a/tests/ui/associated-types/assoc-type-outlives-via-where-clause-63253.rs b/tests/ui/associated-types/assoc-type-outlives-via-where-clause-63253.rs new file mode 100644 index 0000000000000..ad675d4bc848e --- /dev/null +++ b/tests/ui/associated-types/assoc-type-outlives-via-where-clause-63253.rs @@ -0,0 +1,33 @@ +//! Regression test for . +//! +//! A `where Self::Ty: 'a` bound on the callee was not being used to prove the +//! associated type outlives `'a` at the call site, so both of these calls used +//! to fail with E0309 ("the associated type `>::Ty` may not live +//! long enough"). + +//@ check-pass + +#![allow(unused)] + +// The associated function is reached through a method-call path. +trait Trait<'a> { + type Ty; + fn method(ty_ref: &'a Self::Ty) where Self::Ty: 'a {} +} + +fn caller<'a, T: Trait<'a>>(arg: &'a T::Ty) where T::Ty: 'a { + T::method(arg) +} + +// The same bound, reached through a free function instead. +trait Trait2<'a> { + type Ty; +} + +fn free_fn<'a, T: Trait2<'a>>(_arg: &'a T::Ty) where T::Ty: 'a {} + +fn free_fn_caller<'a, T: Trait2<'a>>(arg: &'a T::Ty) where T::Ty: 'a { + free_fn::(arg) +} + +fn main() {} diff --git a/tests/ui/async-await/async-drop/async-drop-future-drop-poll.rs b/tests/ui/async-await/async-drop/async-drop-future-drop-poll.rs new file mode 100644 index 0000000000000..7886cedd3a10a --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-drop-poll.rs @@ -0,0 +1,17 @@ +// Regression test for #142559 +//@ build-pass +//@ compile-flags: --crate-type=lib +#![feature(async_drop)] +#![allow(incomplete_features)] + +//@ edition: 2024 + +async fn run(f: impl Fn() -> F) { + f().await; +} + +pub async fn async_drop_async_closure() { + let x = async || async {}.await; + + run(x).await; +} diff --git a/tests/ui/borrowck/borrowck-for-loop-head-linkage.stderr b/tests/ui/borrowck/borrowck-for-loop-head-linkage.stderr index f47dce453696e..8cdb2a0f8878d 100644 --- a/tests/ui/borrowck/borrowck-for-loop-head-linkage.stderr +++ b/tests/ui/borrowck/borrowck-for-loop-head-linkage.stderr @@ -1,26 +1,38 @@ error[E0502]: cannot borrow `vector` as mutable because it is also borrowed as immutable --> $DIR/borrowck-for-loop-head-linkage.rs:7:9 | -LL | for &x in &vector { - | ------- - | | - | immutable borrow occurs here - | immutable borrow later used here -LL | let cap = vector.capacity(); -LL | vector.extend(repeat(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | for &x in &vector { + | - ------- + | | | + | | immutable borrow occurs here + | _____| immutable borrow later used here + | | +LL | | let cap = vector.capacity(); +LL | | vector.extend(repeat(0)); + | | ^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | | vector[1] = 5; +LL | | } + | |_____- this for loop borrows `vector` immutably, preventing mutation within its body + | + = help: consider using an index-based loop instead, or collecting modifications into a separate collection error[E0502]: cannot borrow `vector` as mutable because it is also borrowed as immutable --> $DIR/borrowck-for-loop-head-linkage.rs:8:9 | -LL | for &x in &vector { - | ------- - | | - | immutable borrow occurs here - | immutable borrow later used here -... -LL | vector[1] = 5; - | ^^^^^^ mutable borrow occurs here +LL | for &x in &vector { + | - ------- + | | | + | | immutable borrow occurs here + | _____| immutable borrow later used here + | | +LL | | let cap = vector.capacity(); +LL | | vector.extend(repeat(0)); +LL | | vector[1] = 5; + | | ^^^^^^ mutable borrow occurs here +LL | | } + | |_____- this for loop borrows `vector` immutably, preventing mutation within its body + | + = help: consider using an index-based loop instead, or collecting modifications into a separate collection error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/issue-82462.stderr b/tests/ui/borrowck/issue-82462.stderr index 8cb4583eba940..ed5a4cc2d219f 100644 --- a/tests/ui/borrowck/issue-82462.stderr +++ b/tests/ui/borrowck/issue-82462.stderr @@ -1,17 +1,22 @@ error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable --> $DIR/issue-82462.rs:18:9 | -LL | for x in DroppingSlice(&*v).iter() { - | ------------------ - | | | - | | immutable borrow occurs here - | a temporary with access to the immutable borrow is created here ... -LL | v.push(*x); - | ^^^^^^^^^^ mutable borrow occurs here -LL | break; -LL | } - | - ... and the immutable borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DroppingSlice` +LL | for x in DroppingSlice(&*v).iter() { + | - ------------------ + | | | | + | | | immutable borrow occurs here + | _____| a temporary with access to the immutable borrow is created here ... + | | +LL | | v.push(*x); + | | ^^^^^^^^^^ mutable borrow occurs here +LL | | break; +LL | | } + | | - + | | | + | |_____... and the immutable borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DroppingSlice` + | this for loop borrows `v` immutably, preventing mutation within its body | + = help: consider using an index-based loop instead, or collecting modifications into a separate collection help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped | LL | }; diff --git a/tests/ui/borrowck/mutate-vec-while-iterating.rs b/tests/ui/borrowck/mutate-vec-while-iterating.rs new file mode 100644 index 0000000000000..18cc906ee500b --- /dev/null +++ b/tests/ui/borrowck/mutate-vec-while-iterating.rs @@ -0,0 +1,21 @@ +// Regression test for https://github.com/rust-lang/rust/issues/159489 + +fn main() { + let mut values = vec![1, 2, 3]; + + for value in &values { + if *value == 2 { + values.push(4); //~ ERROR E0502 + } + } +} + +fn mutate_while_iterating_mut() { + let mut values = vec![1, 2, 3]; + + for value in &mut values { + if *value == 2 { + values.push(4); //~ ERROR E0499 + } + } +} diff --git a/tests/ui/borrowck/mutate-vec-while-iterating.stderr b/tests/ui/borrowck/mutate-vec-while-iterating.stderr new file mode 100644 index 0000000000000..01f42e9689ab7 --- /dev/null +++ b/tests/ui/borrowck/mutate-vec-while-iterating.stderr @@ -0,0 +1,40 @@ +error[E0502]: cannot borrow `values` as mutable because it is also borrowed as immutable + --> $DIR/mutate-vec-while-iterating.rs:8:13 + | +LL | for value in &values { + | - ------- + | | | + | | immutable borrow occurs here + | _____| immutable borrow later used here + | | +LL | | if *value == 2 { +LL | | values.push(4); + | | ^^^^^^^^^^^^^^ mutable borrow occurs here +LL | | } +LL | | } + | |_____- this for loop borrows `values` immutably, preventing mutation within its body + | + = help: consider using an index-based loop instead, or collecting modifications into a separate collection + +error[E0499]: cannot borrow `values` as mutable more than once at a time + --> $DIR/mutate-vec-while-iterating.rs:18:13 + | +LL | for value in &mut values { + | - ----------- + | | | + | | first mutable borrow occurs here + | _____| first borrow later used here + | | +LL | | if *value == 2 { +LL | | values.push(4); + | | ^^^^^^ second mutable borrow occurs here +LL | | } +LL | | } + | |_____- this for loop borrows `values` mutably, preventing mutation within its body + | + = help: consider using an index-based loop instead, or collecting modifications into a separate collection + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0499, E0502. +For more information about an error, try `rustc --explain E0499`. diff --git a/tests/ui/c-variadic/not-async.stderr b/tests/ui/c-variadic/not-async.stderr index 921210382236c..9a81e0ce270d6 100644 --- a/tests/ui/c-variadic/not-async.stderr +++ b/tests/ui/c-variadic/not-async.stderr @@ -14,21 +14,19 @@ error[E0700]: hidden type for `impl Future` captures lifetime that --> $DIR/not-async.rs:4:65 | LL | async unsafe extern "C" fn fn_cannot_be_async(x: isize, _: ...) {} - | -^^ - | | - | opaque type defined here - | - = note: hidden type `{async fn body of fn_cannot_be_async()}` captures lifetime `'_` + | ----------------------------------------------------------------^^ + | | | + | | opaque type defined here + | hidden type `{async fn body of fn_cannot_be_async()}` captures the anonymous lifetime as defined here error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds --> $DIR/not-async.rs:11:73 | LL | async unsafe extern "C" fn method_cannot_be_async(x: isize, _: ...) {} - | -^^ - | | - | opaque type defined here - | - = note: hidden type `{async fn body of S::method_cannot_be_async()}` captures lifetime `'_` + | --------------------------------------------------------------------^^ + | | | + | | opaque type defined here + | hidden type `{async fn body of S::method_cannot_be_async()}` captures the anonymous lifetime as defined here error: aborting due to 4 previous errors diff --git a/tests/ui/c-variadic/variadic-ffi-4.stderr b/tests/ui/c-variadic/variadic-ffi-4.stderr index d53f1f527748c..a92a5fd4bf61d 100644 --- a/tests/ui/c-variadic/variadic-ffi-4.stderr +++ b/tests/ui/c-variadic/variadic-ffi-4.stderr @@ -30,9 +30,9 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:21:5 | LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { - | ------- ------- has type `VaList<'1>` + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaList<'2>` + | has type `&mut VaList<'1>` LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2` | @@ -44,9 +44,9 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:21:5 | LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { - | ------- ------- has type `VaList<'1>` + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaList<'2>` + | has type `&mut VaList<'1>` LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'2` must outlive `'1` | diff --git a/tests/ui/cfg/cfg-stmt-recovery.rs b/tests/ui/cfg/cfg-stmt-recovery.rs index f0f9a649165b5..98f79cd8cfc1c 100644 --- a/tests/ui/cfg/cfg-stmt-recovery.rs +++ b/tests/ui/cfg/cfg-stmt-recovery.rs @@ -1,7 +1,7 @@ // Verify that we do not ICE when failing to parse a statement in `cfg_eval`. #![feature(cfg_eval)] -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, proc_macro_hygiene)] #[cfg_eval] fn main() { diff --git a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs index 7c42be3ed4d6e..3f6f902cf3688 100644 --- a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs +++ b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs @@ -3,7 +3,7 @@ //@ check-pass #![feature(cfg_eval)] -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, proc_macro_hygiene)] fn f() -> u32 { #[cfg_eval] #[cfg(not(FALSE))] 0 diff --git a/tests/ui/delegation/self-mapping-output-from-wrap-errors.rs b/tests/ui/delegation/self-mapping-output-from-wrap-errors.rs new file mode 100644 index 0000000000000..6ef2a4b72559c --- /dev/null +++ b/tests/ui/delegation/self-mapping-output-from-wrap-errors.rs @@ -0,0 +1,72 @@ +#![feature(fn_delegation)] + +mod pin_box_self { + use std::pin::Pin; + + trait MyAdd { + fn add(self, other: Self) -> Pin>; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Pin> { + Pin::new(Box::new(self + other)) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Pin>); + + reuse impl MyAdd for W { + //~^ ERROR: the trait bound `Pin>: From` is not satisfied + *self.0 + } +} + +mod many_froms { + use std::sync::Arc; + use std::rc::Rc; + + trait MyAdd { + fn add(self, other: Self) -> Box>>>>>; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Box>>>>> { + Box::new(Box::new(Box::new(Arc::new(Box::new(Rc::new(self + other)))))) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Box>>>>>); + + reuse impl MyAdd for W { + //~^ ERROR: the trait bound `Box>>>>>: From` is not satisfied + ******self.0 + } +} + +mod many_froms_2 { + use std::sync::Arc; + use std::rc::Rc; + + trait MyAdd { + fn add(self, other: Self) -> Box>>>>; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Box>>>> { + Box::new(Arc::new(Rc::new(Box::new(Rc::new(self + other))))) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Box>>>>); + + reuse impl MyAdd for W { + //~^ ERROR: the trait bound `Box>>>>: From` is not satisfied + *****self.0 + } +} + +fn main() { +} diff --git a/tests/ui/delegation/self-mapping-output-from-wrap-errors.stderr b/tests/ui/delegation/self-mapping-output-from-wrap-errors.stderr new file mode 100644 index 0000000000000..d6290bc220966 --- /dev/null +++ b/tests/ui/delegation/self-mapping-output-from-wrap-errors.stderr @@ -0,0 +1,57 @@ +error[E0277]: the trait bound `Pin>: From` is not satisfied + --> $DIR/self-mapping-output-from-wrap-errors.rs:19:5 + | +LL | / reuse impl MyAdd for W { +LL | | +LL | | *self.0 +LL | | } + | |_____^ the trait `From` is not implemented for `Pin>` + | +help: the trait `From` is not implemented for `Pin>` + but trait `From>` is implemented for it + --> $SRC_DIR/alloc/src/boxed/convert.rs:LL:COL + = help: for that trait implementation, expected `Box`, found `pin_box_self::W` + +error[E0277]: the trait bound `Box>>>>>: From` is not satisfied + --> $DIR/self-mapping-output-from-wrap-errors.rs:42:5 + | +LL | / reuse impl MyAdd for W { +LL | | +LL | | ******self.0 +LL | | } + | |_____^ the trait `From` is not implemented for `Box>>>>>` + | + = help: the following other types implement trait `From`: + `Box` implements `From>` + `Box` implements `From<&CStr>` + `Box` implements `From<&mut CStr>` + `Box` implements `From` + `Box` implements `From>` + `Box` implements `From<&OsStr>` + `Box` implements `From<&mut OsStr>` + `Box` implements `From>` + and 25 others + +error[E0277]: the trait bound `Box>>>>: From` is not satisfied + --> $DIR/self-mapping-output-from-wrap-errors.rs:65:5 + | +LL | / reuse impl MyAdd for W { +LL | | +LL | | *****self.0 +LL | | } + | |_____^ the trait `From` is not implemented for `Box>>>>` + | + = help: the following other types implement trait `From`: + `Box` implements `From>` + `Box` implements `From<&CStr>` + `Box` implements `From<&mut CStr>` + `Box` implements `From` + `Box` implements `From>` + `Box` implements `From<&OsStr>` + `Box` implements `From<&mut OsStr>` + `Box` implements `From>` + and 25 others + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/delegation/self-mapping-output-from-wrap.rs b/tests/ui/delegation/self-mapping-output-from-wrap.rs new file mode 100644 index 0000000000000..2dfff2882fbc6 --- /dev/null +++ b/tests/ui/delegation/self-mapping-output-from-wrap.rs @@ -0,0 +1,199 @@ +//@ run-pass +//@ check-run-results + +#![feature(fn_delegation)] + +mod simple_self { + trait MyAdd { + fn add(self, other: Self) -> Self; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> usize { + self + other + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(usize); + + reuse impl MyAdd for W { + println!("simple_self {self:?}"); + self.0 + } + + pub fn check() { + assert_eq!(W(1).add(W(2)), W(3)) + } +} + +mod box_self { + trait MyAdd { + fn add(self, other: Self) -> Box; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Box { + Box::new(self + other) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Box); + + reuse impl MyAdd for W { + println!("box_self {self:?}"); + *self.0 + } + + pub fn check() { + fn w(x: usize) -> W { + W(Box::new(x)) + } + + assert_eq!(w(1).add(w(2)), Box::new(w(3))) + } +} + +mod rc_self { + use std::rc::Rc; + + trait MyAdd { + fn add(self, other: Self) -> Rc; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Rc { + Rc::new(self + other) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Rc); + + reuse impl MyAdd for W { + println!("rc_self {self:?}"); + *self.0 + } + + pub fn check() { + fn w(x: usize) -> W { + W(Rc::new(x)) + } + + assert_eq!(w(1).add(w(2)), Rc::new(w(3))) + } +} + +mod arc_self { + use std::sync::Arc; + + trait MyAdd { + fn add(self, other: Self) -> Arc; + } + + impl MyAdd for usize { + fn add(self, other: usize) -> Arc { + Arc::new(self + other) + } + } + + #[derive(Eq, PartialEq, Debug)] + struct W(Arc); + + reuse impl MyAdd for W { + println!("arc_self {self:?}"); + *self.0 + } + + pub fn check() { + fn w(x: usize) -> W { + W(Arc::new(x)) + } + + assert_eq!(w(1).add(w(2)), Arc::new(w(3))) + } +} + +mod custom_froms { + #[derive(Debug)] + struct S1 { + a: A, + } + + impl From for S1 { + fn from(a: A) -> S1 { + S1 { a } + } + } + + #[derive(Debug)] + struct S2 { + t: T, + } + + impl From for S2 { + fn from(t: T) -> S2 { + S2 { t } + } + } + + #[derive(Debug)] + struct S3<'a, const C: usize, T, U, const B: bool> { + t: T, + pd: std::marker::PhantomData<&'a [(usize, U); C]> + } + + impl<'a, const C: usize, T, const B: bool> From for S3<'a, C, T, (), B> { + fn from(t: T) -> S3<'a, C, T, (), B> { + S3 { + t, + pd: std::marker::PhantomData::<&'a [(usize, ()); C]>, + } + } + } + + trait MyAdd: Sized { + fn add(self, other: Self) -> S1>>, (), true>>>; + } + + fn create_monster_struct(x: T) -> S1>>, (), true>>> { + S1::from(S1::from(S3::from(S2::from(S2::from(S1::from(x)))))) + } + + impl MyAdd for usize { + fn add(self, other: usize) -> S1>>, (), true>>> { + create_monster_struct(self + other) + } + } + + #[derive(Debug)] + struct W(S1>>, (), true>>>); + + impl From for S1>>, (), true>>> { + fn from(x: W) -> Self { + create_monster_struct(x) + } + } + + reuse impl MyAdd for W { + println!("custom_froms {self:?}"); + self.0.a.a.t.t.t.a + } + + pub fn check() { + fn w(x: usize) -> W { + W(create_monster_struct(x)) + } + + assert_eq!(w(1).add(w(2)).a.a.t.t.t.a.0.a.a.t.t.t.a, 3) + } +} + +fn main() { + simple_self::check(); + box_self::check(); + rc_self::check(); + arc_self::check(); + custom_froms::check(); +} diff --git a/tests/ui/delegation/self-mapping-output-from-wrap.run.stdout b/tests/ui/delegation/self-mapping-output-from-wrap.run.stdout new file mode 100644 index 0000000000000..ee96199c54e07 --- /dev/null +++ b/tests/ui/delegation/self-mapping-output-from-wrap.run.stdout @@ -0,0 +1,10 @@ +simple_self W(1) +simple_self W(2) +box_self W(1) +box_self W(2) +rc_self W(1) +rc_self W(2) +arc_self W(1) +arc_self W(2) +custom_froms W(S1 { a: S1 { a: S3 { t: S2 { t: S2 { t: S1 { a: 1 } } }, pd: PhantomData<&[(usize, ()); 123]> } } }) +custom_froms W(S1 { a: S1 { a: S3 { t: S2 { t: S2 { t: S1 { a: 2 } } }, pd: PhantomData<&[(usize, ()); 123]> } } }) diff --git a/tests/ui/delegation/wrong-fn-kind-ice-159127.rs b/tests/ui/delegation/wrong-fn-kind-ice-159127.rs new file mode 100644 index 0000000000000..e117826ef099c --- /dev/null +++ b/tests/ui/delegation/wrong-fn-kind-ice-159127.rs @@ -0,0 +1,15 @@ +#![feature(fn_delegation)] +#![feature(min_generic_const_args)] + +impl + core::direct_const_arg!({ + //~^ ERROR: expected type, found `direct_const_arg!()` constant + fn foo() {} + reuse foo::<>as bar; + reuse bar; + //~^ ERROR: the name `bar` is defined multiple times + }) +{ +} + +fn main() {} diff --git a/tests/ui/delegation/wrong-fn-kind-ice-159127.stderr b/tests/ui/delegation/wrong-fn-kind-ice-159127.stderr new file mode 100644 index 0000000000000..bc218f39ab65a --- /dev/null +++ b/tests/ui/delegation/wrong-fn-kind-ice-159127.stderr @@ -0,0 +1,24 @@ +error[E0428]: the name `bar` is defined multiple times + --> $DIR/wrong-fn-kind-ice-159127.rs:9:9 + | +LL | reuse foo::<>as bar; + | -------------------- previous definition of the value `bar` here +LL | reuse bar; + | ^^^^^^^^^^ `bar` redefined here + | + = note: `bar` must be defined only once in the value namespace of this block + +error: expected type, found `direct_const_arg!()` constant + --> $DIR/wrong-fn-kind-ice-159127.rs:5:5 + | +LL | / core::direct_const_arg!({ +LL | | +LL | | fn foo() {} +LL | | reuse foo::<>as bar; +... | +LL | | }) + | |______^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0428`. diff --git a/tests/ui/eii/errors.rs b/tests/ui/eii/errors.rs index bc6c17f463a78..b3bb4cc031bd8 100644 --- a/tests/ui/eii/errors.rs +++ b/tests/ui/eii/errors.rs @@ -8,7 +8,7 @@ #[eii_declaration(bar)] //~ ERROR `#[eii_declaration(...)]` is only valid on macros fn hello() { #[eii_declaration(bar)] //~ ERROR `#[eii_declaration(...)]` is only valid on macros - let x = 3 + 3; + let x = 3 + 3; //~| ERROR macro attributes on statements are unstable } #[eii_declaration] //~ ERROR `#[eii_declaration(...)]` expects a list of one or two elements diff --git a/tests/ui/eii/errors.stderr b/tests/ui/eii/errors.stderr index 553ae622cb36f..28411cfd5108a 100644 --- a/tests/ui/eii/errors.stderr +++ b/tests/ui/eii/errors.stderr @@ -4,6 +4,16 @@ error: `#[eii_declaration(...)]` is only valid on macros LL | #[eii_declaration(bar)] | ^^^^^^^^^^^^^^^^^^^^^^^ +error[E0658]: macro attributes on statements are unstable + --> $DIR/errors.rs:10:5 + | +LL | #[eii_declaration(bar)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #54727 for more information + = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error: `#[eii_declaration(...)]` is only valid on macros --> $DIR/errors.rs:10:5 | @@ -88,5 +98,6 @@ error: `#[foo]` expected no arguments or a single argument: `#[foo(default)]` LL | #[foo = "default"] | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr index 593120edd1700..fa42547a6b652 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr @@ -1,9 +1,10 @@ warning: unknown diagnostic attribute - --> $DIR/feature-gate-diagnostic-on-move.rs:5:15 + --> $DIR/feature-gate-diagnostic-on-move.rs:5:1 | LL | #[diagnostic::on_move(message = "Foo")] - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this is an experimental diagnostic attribute = help: add `#![feature(diagnostic_on_move)]` to the crate attributes to enable = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error-malformed-args.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error-malformed-args.stderr index c8b4aac78d6e1..3964152f3d4cf 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error-malformed-args.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error-malformed-args.stderr @@ -1,9 +1,10 @@ warning: unknown diagnostic attribute - --> $DIR/feature-gate-diagnostic-on-type-error-malformed-args.rs:5:15 + --> $DIR/feature-gate-diagnostic-on-type-error-malformed-args.rs:5:1 | LL | #[diagnostic::on_type_error(unknown = "")] - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this is an experimental diagnostic attribute = help: add `#![feature(diagnostic_on_type_error)]` to the crate attributes to enable = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.rs index b17e6b57ef7da..355af18939e83 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.rs +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.rs @@ -2,6 +2,7 @@ #[diagnostic::on_type_error(note = "custom on_type_error note: expected {Expected}, found {Found}")] //~^ WARN unknown diagnostic attribute +//~| NOTE this is an experimental diagnostic attribute //~| NOTE `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default #[derive(Debug)] struct Foo(T); diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.stderr index 72f5cd932124f..f58f379aa13d0 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error.stderr @@ -1,14 +1,15 @@ warning: unknown diagnostic attribute - --> $DIR/feature-gate-diagnostic-on-type-error.rs:3:15 + --> $DIR/feature-gate-diagnostic-on-type-error.rs:3:1 | LL | #[diagnostic::on_type_error(note = "custom on_type_error note: expected {Expected}, found {Found}")] - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this is an experimental diagnostic attribute = help: add `#![feature(diagnostic_on_type_error)]` to the crate attributes to enable = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default error[E0308]: mismatched types - --> $DIR/feature-gate-diagnostic-on-type-error.rs:14:15 + --> $DIR/feature-gate-diagnostic-on-type-error.rs:15:15 | LL | takes_foo(foo); | --------- ^^^ expected `Foo`, found `Foo` @@ -18,7 +19,7 @@ LL | takes_foo(foo); = note: expected struct `Foo` found struct `Foo` note: function defined here - --> $DIR/feature-gate-diagnostic-on-type-error.rs:9:4 + --> $DIR/feature-gate-diagnostic-on-type-error.rs:10:4 | LL | fn takes_foo(_: Foo) {} | ^^^^^^^^^ ----------- diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr index 6e9d35a09821b..d9a22ccf9eaa6 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr @@ -7,11 +7,12 @@ LL | use std::vec::NotExisting; | no `NotExisting` in `vec` error: unknown diagnostic attribute - --> $DIR/feature-gate-diagnostic-on-unknown.rs:3:15 + --> $DIR/feature-gate-diagnostic-on-unknown.rs:3:1 | LL | #[diagnostic::on_unknown(message = "Tada")] - | ^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this is an experimental diagnostic attribute = help: add `#![feature(diagnostic_on_unknown)]` to the crate attributes to enable note: the lint level is defined here --> $DIR/feature-gate-diagnostic-on-unknown.rs:1:9 diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr index 90426a1324e81..3de2036717909 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr @@ -1,9 +1,10 @@ error: unknown diagnostic attribute - --> $DIR/feature-gate-diagnostic-opaque.rs:5:15 + --> $DIR/feature-gate-diagnostic-opaque.rs:5:1 | LL | #[diagnostic::opaque] - | ^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ | + = note: this is an experimental diagnostic attribute = help: add `#![feature(diagnostic_opaque)]` to the crate attributes to enable note: the lint level is defined here --> $DIR/feature-gate-diagnostic-opaque.rs:3:9 @@ -12,11 +13,12 @@ LL | #![deny(unknown_diagnostic_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unknown diagnostic attribute - --> $DIR/feature-gate-diagnostic-opaque.rs:11:15 + --> $DIR/feature-gate-diagnostic-opaque.rs:11:1 | LL | #[diagnostic::opaque] - | ^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ | + = note: this is an experimental diagnostic attribute = help: add `#![feature(diagnostic_opaque)]` to the crate attributes to enable error: aborting due to 2 previous errors diff --git a/tests/ui/feature-gates/unknown-feature-with-other-errors-58390.rs b/tests/ui/feature-gates/unknown-feature-with-other-errors-58390.rs new file mode 100644 index 0000000000000..f14c0937360f4 --- /dev/null +++ b/tests/ui/feature-gates/unknown-feature-with-other-errors-58390.rs @@ -0,0 +1,16 @@ +//! Regression test for . +//! +//! An unknown `#![feature(..)]` name used to be silently ignored whenever the crate had any +//! other error, because the check only ran during stability checking. Both errors must be +//! reported. + +#![feature(this_feature_does_not_exist)] //~ ERROR unknown feature `this_feature_does_not_exist` + +struct Foo; + +trait Bar {} + +impl Bar for Foo {} +impl Bar for Foo {} //~ ERROR conflicting implementations of trait `Bar` for type `Foo` + +fn main() {} diff --git a/tests/ui/feature-gates/unknown-feature-with-other-errors-58390.stderr b/tests/ui/feature-gates/unknown-feature-with-other-errors-58390.stderr new file mode 100644 index 0000000000000..4560bd48f58ce --- /dev/null +++ b/tests/ui/feature-gates/unknown-feature-with-other-errors-58390.stderr @@ -0,0 +1,18 @@ +error[E0635]: unknown feature `this_feature_does_not_exist` + --> $DIR/unknown-feature-with-other-errors-58390.rs:7:12 + | +LL | #![feature(this_feature_does_not_exist)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0119]: conflicting implementations of trait `Bar` for type `Foo` + --> $DIR/unknown-feature-with-other-errors-58390.rs:14:1 + | +LL | impl Bar for Foo {} + | ---------------- first implementation here +LL | impl Bar for Foo {} + | ^^^^^^^^^^^^^^^^ conflicting implementation for `Foo` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0119, E0635. +For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/generic-associated-types/gat-bound-mismatch-106832.rs b/tests/ui/generic-associated-types/gat-bound-mismatch-106832.rs new file mode 100644 index 0000000000000..943bc3b32b4b1 --- /dev/null +++ b/tests/ui/generic-associated-types/gat-bound-mismatch-106832.rs @@ -0,0 +1,30 @@ +//! Regression test for . +//! +//! Proving `T::Assoc<_>: Sized` while a where-clause bound `T::Assoc: Sized` is +//! in scope used to over-eagerly infer the otherwise-unconstrained argument to `u8`, +//! producing a spurious "mismatched types" error. This should compile. + +//@ check-pass + +#![allow(dead_code)] + +trait Trait { + type Assoc; +} + +fn test() +where + T::Assoc: Sized, +{ + // `_` must be inferred from the `1i32` argument, not eagerly unified with `u8` + // just because `T::Assoc: Sized` happens to be in the environment. + constrain::(1i32); +} + +fn constrain(_: A) +where + T::Assoc: Sized, +{ +} + +fn main() {} diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.stderr b/tests/ui/inference/note-and-explain-ReVar-124973.stderr index 3610fa82754b9..3ba76eb2ece18 100644 --- a/tests/ui/inference/note-and-explain-ReVar-124973.stderr +++ b/tests/ui/inference/note-and-explain-ReVar-124973.stderr @@ -8,11 +8,10 @@ error[E0700]: hidden type for `impl Future` captures lifetime that --> $DIR/note-and-explain-ReVar-124973.rs:3:76 | LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, _: ...) {} - | -^^ - | | - | opaque type defined here - | - = note: hidden type `{async fn body of multiple_named_lifetimes<'a, 'b>()}` captures lifetime `'_` + | ---------------------------------------------------------------------------^^ + | | | + | | opaque type defined here + | hidden type `{async fn body of multiple_named_lifetimes<'a, 'b>()}` captures the anonymous lifetime as defined here error: aborting due to 2 previous errors diff --git a/tests/ui/macros/issue-111749.rs b/tests/ui/macros/issue-111749.rs index f009a69fe2535..7c10038925111 100644 --- a/tests/ui/macros/issue-111749.rs +++ b/tests/ui/macros/issue-111749.rs @@ -9,4 +9,5 @@ fn main() { //~^ ERROR the `test` attribute may only be used on a free function //~| ERROR attribute must be of the form `#[test]` //~| WARNING this was previously accepted by the compiler but is being phased out + //~| ERROR macro attributes on expressions are unstable } diff --git a/tests/ui/macros/issue-111749.stderr b/tests/ui/macros/issue-111749.stderr index 267f939602b5b..3207aa182abec 100644 --- a/tests/ui/macros/issue-111749.stderr +++ b/tests/ui/macros/issue-111749.stderr @@ -1,3 +1,13 @@ +error[E0658]: macro attributes on expressions are unstable + --> $DIR/issue-111749.rs:8:17 + | +LL | cbor_map! { #[test(test)] 4i32}; + | ^^^^^^^^^^^^^ + | + = note: see issue #54727 for more information + = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error: the `test` attribute may only be used on a free function --> $DIR/issue-111749.rs:8:17 | @@ -20,8 +30,9 @@ LL | cbor_map! { #[test(test)] 4i32}; = note: for more information, see issue #57571 = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0658`. Future incompatibility report: Future breakage diagnostic: error: attribute must be of the form `#[test]` --> $DIR/issue-111749.rs:8:17 diff --git a/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.nll.stderr b/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.nll.stderr index 374e806ba0347..0e5ed61f6d058 100644 --- a/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.nll.stderr +++ b/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.nll.stderr @@ -1,11 +1,20 @@ error[E0499]: cannot borrow `*elements` as mutable more than once at a time --> $DIR/iterating-updating-cursor-issue-108704.rs:42:26 | -LL | for (idx, el) in elements.iter_mut().enumerate() { - | ^^^^^^^^ - | | - | `*elements` was mutably borrowed here in the previous iteration of the loop - | first borrow used here, in later iteration of loop +LL | for (idx, el) in elements.iter_mut().enumerate() { + | - ^^^^^^^^ + | | | + | | `*elements` was mutably borrowed here in the previous iteration of the loop + | _________| first borrow used here, in later iteration of loop + | | +LL | | if el.name == *p { +LL | | elements = &mut el.children; +LL | | break; +LL | | } +LL | | } + | |_________- this for loop borrows `*elements` mutably, preventing mutation within its body + | + = help: consider using an index-based loop instead, or collecting modifications into a separate collection error: aborting due to 1 previous error diff --git a/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.polonius.stderr b/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.polonius.stderr index 374e806ba0347..0e5ed61f6d058 100644 --- a/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.polonius.stderr +++ b/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.polonius.stderr @@ -1,11 +1,20 @@ error[E0499]: cannot borrow `*elements` as mutable more than once at a time --> $DIR/iterating-updating-cursor-issue-108704.rs:42:26 | -LL | for (idx, el) in elements.iter_mut().enumerate() { - | ^^^^^^^^ - | | - | `*elements` was mutably borrowed here in the previous iteration of the loop - | first borrow used here, in later iteration of loop +LL | for (idx, el) in elements.iter_mut().enumerate() { + | - ^^^^^^^^ + | | | + | | `*elements` was mutably borrowed here in the previous iteration of the loop + | _________| first borrow used here, in later iteration of loop + | | +LL | | if el.name == *p { +LL | | elements = &mut el.children; +LL | | break; +LL | | } +LL | | } + | |_________- this for loop borrows `*elements` mutably, preventing mutation within its body + | + = help: consider using an index-based loop instead, or collecting modifications into a separate collection error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/cfg-eval-fail.rs b/tests/ui/proc-macro/cfg-eval-fail.rs index a94dcd2837811..d9256cfa3377d 100644 --- a/tests/ui/proc-macro/cfg-eval-fail.rs +++ b/tests/ui/proc-macro/cfg-eval-fail.rs @@ -4,4 +4,5 @@ fn main() { let _ = #[cfg_eval] #[cfg(false)] 0; //~^ ERROR removing an expression is not supported in this position + //~| ERROR macro attributes on expressions are unstable } diff --git a/tests/ui/proc-macro/cfg-eval-fail.stderr b/tests/ui/proc-macro/cfg-eval-fail.stderr index 7f21e4646b1cc..6cd3f54d6fafd 100644 --- a/tests/ui/proc-macro/cfg-eval-fail.stderr +++ b/tests/ui/proc-macro/cfg-eval-fail.stderr @@ -4,5 +4,16 @@ error: removing an expression is not supported in this position LL | let _ = #[cfg_eval] #[cfg(false)] 0; | ^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error[E0658]: macro attributes on expressions are unstable + --> $DIR/cfg-eval-fail.rs:5:13 + | +LL | let _ = #[cfg_eval] #[cfg(false)] 0; + | ^^^^^^^^^^^ + | + = note: see issue #54727 for more information + = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/proc-macro/derive-macro-invalid-placement.rs b/tests/ui/proc-macro/derive-macro-invalid-placement.rs index fd24bd7284a92..463e7dc758505 100644 --- a/tests/ui/proc-macro/derive-macro-invalid-placement.rs +++ b/tests/ui/proc-macro/derive-macro-invalid-placement.rs @@ -1,6 +1,6 @@ //! regression test for -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, proc_macro_hygiene)] fn foo<#[derive(Debug)] T>() { //~ ERROR expected non-macro attribute, found attribute macro match 0 { diff --git a/tests/ui/proc-macro/proc-macro-gates.rs b/tests/ui/proc-macro/proc-macro-gates.rs index 04e097eb2f745..a201836851761 100644 --- a/tests/ui/proc-macro/proc-macro-gates.rs +++ b/tests/ui/proc-macro/proc-macro-gates.rs @@ -23,26 +23,26 @@ fn attrs() { struct S; // Statement, macro - #[empty_attr] //~ ERROR: custom attributes cannot be applied to statements + #[empty_attr] //~ ERROR: macro attributes on statements are unstable println!(); // Statement, semi - #[empty_attr] //~ ERROR: custom attributes cannot be applied to statements + #[empty_attr] //~ ERROR: macro attributes on statements are unstable S; // Statement, local - #[empty_attr] //~ ERROR: custom attributes cannot be applied to statements + #[empty_attr] //~ ERROR: macro attributes on statements are unstable let _x = 2; // Expr - let _x = #[identity_attr] 2; //~ ERROR: custom attributes cannot be applied to expressions + let _x = #[identity_attr] 2; //~ ERROR: macro attributes on expressions are unstable // Opt expr - let _x = [#[identity_attr] 2]; //~ ERROR: custom attributes cannot be applied to expressions + let _x = [#[identity_attr] 2]; //~ ERROR: macro attributes on expressions are unstable // Expr macro let _x = #[identity_attr] println!(); - //~^ ERROR: custom attributes cannot be applied to expressions + //~^ ERROR: macro attributes on expressions are unstable } fn test_case() { diff --git a/tests/ui/proc-macro/proc-macro-gates.stderr b/tests/ui/proc-macro/proc-macro-gates.stderr index 3607b062a5fcb..9a243f4f900b0 100644 --- a/tests/ui/proc-macro/proc-macro-gates.stderr +++ b/tests/ui/proc-macro/proc-macro-gates.stderr @@ -24,7 +24,7 @@ error: key-value macro attributes are not supported LL | #[empty_attr = "y"] | ^^^^^^^^^^^^^^^^^^^ -error[E0658]: custom attributes cannot be applied to statements +error[E0658]: macro attributes on statements are unstable --> $DIR/proc-macro-gates.rs:26:5 | LL | #[empty_attr] @@ -34,7 +34,7 @@ LL | #[empty_attr] = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: custom attributes cannot be applied to statements +error[E0658]: macro attributes on statements are unstable --> $DIR/proc-macro-gates.rs:30:5 | LL | #[empty_attr] @@ -44,7 +44,7 @@ LL | #[empty_attr] = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: custom attributes cannot be applied to statements +error[E0658]: macro attributes on statements are unstable --> $DIR/proc-macro-gates.rs:34:5 | LL | #[empty_attr] @@ -54,7 +54,7 @@ LL | #[empty_attr] = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: custom attributes cannot be applied to expressions +error[E0658]: macro attributes on expressions are unstable --> $DIR/proc-macro-gates.rs:38:14 | LL | let _x = #[identity_attr] 2; @@ -64,7 +64,7 @@ LL | let _x = #[identity_attr] 2; = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: custom attributes cannot be applied to expressions +error[E0658]: macro attributes on expressions are unstable --> $DIR/proc-macro-gates.rs:41:15 | LL | let _x = [#[identity_attr] 2]; @@ -74,7 +74,7 @@ LL | let _x = [#[identity_attr] 2]; = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: custom attributes cannot be applied to expressions +error[E0658]: macro attributes on expressions are unstable --> $DIR/proc-macro-gates.rs:44:14 | LL | let _x = #[identity_attr] println!(); diff --git a/tests/ui/splat/splat-fn-ptr-cast.rs b/tests/ui/splat/splat-fn-ptr-cast.rs index 6e4a05ac2a776..9b1eac8a6fa85 100644 --- a/tests/ui/splat/splat-fn-ptr-cast.rs +++ b/tests/ui/splat/splat-fn-ptr-cast.rs @@ -8,9 +8,8 @@ fn main() { // Bug #158603 regression test variants #[rustfmt::skip] - let _x: fn(#[rustc_splat] (f32,)) = None.unwrap(); - // FIXME(splat): causes an ICE until #158603 is fixed - //x(1.0); + let x: fn(#[rustc_splat] (f32,)) = None.unwrap(); + x(1.0); let x: fn((i32,)) = None.unwrap(); x((1,)); diff --git a/tests/ui/splat/splat-fn-ptr-generic.rs b/tests/ui/splat/splat-fn-ptr-generic.rs new file mode 100644 index 0000000000000..a41fb0aa2a43a --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-generic.rs @@ -0,0 +1,58 @@ +//! Test using `#[rustc_splat]` on tuple arguments of pointers to generic functions. +//@ run-pass + +#![expect(incomplete_features)] +#![feature(splat, tuple_trait)] + +use std::fmt::Debug; +use std::marker::Tuple; + +fn generic(#[rustc_splat] a: T) -> String { + format!("{a:?}") +} + +// FIXME(rustfmt): the attribute gets deleted by rustfmt +#[rustfmt::skip] +fn main() { + let fn_ptr: fn(#[rustc_splat] (u32, i8)) -> String + = generic as fn(#[rustc_splat] (u32, i8)) -> String; + assert_eq!(fn_ptr(1, -2), "(1, -2)"); + assert_eq!(fn_ptr(1u32, -2i8), "(1, -2)"); + + let fn_ptr: fn(#[rustc_splat] (u32, i8)) -> String + = generic::<(u32, i8)> as fn(#[rustc_splat] (u32, i8)) -> String; + assert_eq!(fn_ptr(1, -2), "(1, -2)"); + assert_eq!(fn_ptr(1u32, -2i8), "(1, -2)"); + + let fn_ptr = generic as fn(#[rustc_splat] (u32, i8)) -> String; + assert_eq!(fn_ptr(1, -2), "(1, -2)"); + assert_eq!(fn_ptr(1u32, -2i8), "(1, -2)"); + + let fn_ptr = generic::<(u32, i8)> as fn(#[rustc_splat] (u32, i8)) -> String; + assert_eq!(fn_ptr(1, -2), "(1, -2)"); + assert_eq!(fn_ptr(1u32, -2i8), "(1, -2)"); + + let fn_ptr: fn(#[rustc_splat] (u32, i8)) -> String = generic as _; + assert_eq!(fn_ptr(1, -2), "(1, -2)"); + assert_eq!(fn_ptr(1u32, -2i8), "(1, -2)"); + + let fn_ptr: fn(#[rustc_splat] (u32, i8)) -> String = generic::<(u32, i8)> as _; + assert_eq!(fn_ptr(1, -2), "(1, -2)"); + assert_eq!(fn_ptr(1u32, -2i8), "(1, -2)"); + + // Now without explicit `as`, this requires turbofish + let fn_ptr: fn(#[rustc_splat] (f64, i8)) -> String = generic::<(f64, i8)>; + assert_eq!(fn_ptr(3.5, -2), "(3.5, -2)"); + assert_eq!(fn_ptr(3.5f64, -2i8), "(3.5, -2)"); + + // FIXME(unused_variables): This is obviously used + #[expect(unused_variables)] + let fn_ptr = generic; + assert_eq!(fn_ptr(-1, 2, 3.5), "(-1, 2, 3.5)"); + assert_eq!(fn_ptr(-1i8, 2u32, 3.5f64), "(-1, 2, 3.5)"); + + #[expect(unused_variables)] + let fn_ptr = generic::<(i8, u32, f64)>; + assert_eq!(fn_ptr(-1, 2, 3.5), "(-1, 2, 3.5)"); + assert_eq!(fn_ptr(-1i8, 2u32, 3.5f64), "(-1, 2, 3.5)"); +} diff --git a/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs index fbe2d8c192f73..6473abce4b750 100644 --- a/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs +++ b/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs @@ -1,43 +1,113 @@ //! Test using `#[rustc_splat]` on tuple arguments of pointers to pointers to simple functions. -//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. +//! Bug #158603 regression test +//@ run-pass -//@ failure-status: 101 - -//@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" -//@ normalize-stderr: "thread.*panicked at .*compiler.*" -> "" -//@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" -//@ normalize-stderr: "note: compiler flags.*\n\n" -> "" -//@ normalize-stderr: " +\d{1,}: .*\n" -> "" -//@ normalize-stderr: " + at .*\n" -> "" -//@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" -//@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" -//@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" - -#![allow(incomplete_features)] +#![expect(incomplete_features)] #![feature(splat)] -fn tuple_args(#[rustc_splat] (_a, _b): (u32, i8)) {} +use std::ptr; + +fn tuple_args(#[rustc_splat] (a, b): (u32, i8)) -> (i8, u32) { + // Permute the returned values as a codegen test + (b, a) +} -fn splat_non_terminal_arg(#[rustc_splat] (_a, _b): (u32, i8), _c: f64) {} +fn splat_non_terminal_arg(#[rustc_splat] (a, b): (u32, i8), c: f64) -> (i8, f64, u32) { + // Permute the returned values as a codegen test + (b, c, a) +} +// FIXME(rustfmt): the attribute gets deleted by rustfmt +#[rustfmt::skip] fn main() { - // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in - // MIR lowering - // FIXME(rustfmt): the attribute gets deleted by rustfmt - #[rustfmt::skip] - let fn_pp: *const fn(#[rustc_splat] (u32, i8)) - = tuple_args as *const fn(#[rustc_splat] (u32, i8)); + let fn_pp: &fn(#[rustc_splat] (u32, i8)) -> (i8, u32) + = &(tuple_args as fn(#[rustc_splat] (u32, i8)) -> (i8, u32)); + assert_eq!((*fn_pp)(1, 2), (2, 1)); + assert_eq!((*fn_pp)(1u32, 2i8), (2i8, 1u32)); + + let fn_pp: &fn(#[rustc_splat] (u32, i8)) -> (i8, u32) = &(tuple_args as _); + assert_eq!((*fn_pp)(1, 2), (2, 1)); + assert_eq!((*fn_pp)(1u32, 2i8), (2i8, 1u32)); + + let fn_pp = &(tuple_args as fn(#[rustc_splat] (u32, i8)) -> (i8, u32)); + assert_eq!((*fn_pp)(1, 2), (2, 1)); + assert_eq!((*fn_pp)(1u32, 2i8), (2i8, 1u32)); + + // FIXME(unused_variables): This is obviously used + #[expect(unused_variables)] + let fn_pp = &tuple_args; + assert_eq!((*fn_pp)(1, 2), (2, 1)); + assert_eq!((*fn_pp)(1u32, 2i8), (2i8, 1u32)); + + // Now with *const + let fn_pp: *const fn(#[rustc_splat] (u32, i8)) -> (i8, u32) + = ptr::from_ref(&(tuple_args as fn(#[rustc_splat] (u32, i8)) -> (i8, u32))); + unsafe { + assert_eq!((*fn_pp)(1, 2), (2, 1)); + assert_eq!((*fn_pp)(1u32, 2i8), (2i8, 1u32)); + } + + let fn_pp: *const fn(#[rustc_splat] (u32, i8)) -> (i8, u32) = ptr::from_ref(&(tuple_args as _)); + unsafe { + assert_eq!((*fn_pp)(1, 2), (2, 1)); + assert_eq!((*fn_pp)(1u32, 2i8), (2i8, 1u32)); + } + + let fn_pp = ptr::from_ref(&(tuple_args as fn(#[rustc_splat] (u32, i8)) -> (i8, u32))); + unsafe { + assert_eq!((*fn_pp)(1, 2), (2, 1)); + assert_eq!((*fn_pp)(1u32, 2i8), (2i8, 1u32)); + } + + #[expect(unused_variables)] + let fn_pp = ptr::from_ref(&tuple_args); + // FIXME(unsafe): dereferencing *const should require unsafe + assert_eq!((*fn_pp)(1, 2), (2, 1)); + assert_eq!((*fn_pp)(1u32, 2i8), (2i8, 1u32)); + + // Now with *mut and non-terminal splat + let fn_pp: *mut fn(#[rustc_splat] (u32, i8), f64) -> (i8, f64, u32) + = ptr::from_mut( + &mut (splat_non_terminal_arg as fn(#[rustc_splat] (u32, i8), f64) -> (i8, f64, u32)) + ); + unsafe { + assert_eq!((*fn_pp)(1, 2, 3.5), (2, 3.5, 1)); + assert_eq!((*fn_pp)(1u32, 2i8, 3.5f64), (2i8, 3.5f64, 1u32)); + } + + let fn_pp: *mut fn(#[rustc_splat] (u32, i8), f64) -> (i8, f64, u32) + = ptr::from_mut(&mut (splat_non_terminal_arg as _)); + unsafe { + assert_eq!((*fn_pp)(1, 2, 3.5), (2, 3.5, 1)); + assert_eq!((*fn_pp)(1u32, 2i8, 3.5f64), (2i8, 3.5f64, 1u32)); + } + + let fn_pp = ptr::from_mut( + &mut (splat_non_terminal_arg as fn(#[rustc_splat] (u32, i8), f64) -> (i8, f64, u32)) + ); + unsafe { + assert_eq!((*fn_pp)(1, 2, 3.5), (2, 3.5, 1)); + assert_eq!((*fn_pp)(1u32, 2i8, 3.5f64), (2i8, 3.5f64, 1u32)); + } + + #[expect(unused_variables)] + let fn_pp = ptr::from_mut(&mut splat_non_terminal_arg); + // FIXME(unsafe): dereferencing *mut should require unsafe + assert_eq!((*fn_pp)(1, 2, 3.5), (2, 3.5, 1)); + assert_eq!((*fn_pp)(1u32, 2i8, 3.5f64), (2i8, 3.5f64, 1u32)); + + // Now with & as *const and non-terminal splat + let fn_pp: *const fn(#[rustc_splat] (u32, i8), f64) -> (i8, f64, u32) + = &(splat_non_terminal_arg as fn(#[rustc_splat] (u32, i8), f64) -> (i8, f64, u32)); unsafe { - (*fn_pp)(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented - // The ICE means that code after this line is not fully checked - (*fn_pp)(1u32, 2i8); + assert_eq!((*fn_pp)(1, 2, 3.5), (2, 3.5, 1)); + assert_eq!((*fn_pp)(1u32, 2i8, 3.5f64), (2i8, 3.5f64, 1u32)); } - #[rustfmt::skip] - let fn_pp: *const fn(#[rustc_splat] (u32, i8), f64) = - splat_non_terminal_arg as *const fn(#[rustc_splat] (u32, i8), f64); + let fn_pp: *const fn(#[rustc_splat] (u32, i8), f64) -> (i8, f64, u32) + = &(splat_non_terminal_arg as _); unsafe { - (*fn_pp)(1, 2, 3.5); - (*fn_pp)(1u32, 2i8, 3.5f64); + assert_eq!((*fn_pp)(1, 2, 3.5), (2, 3.5, 1)); + assert_eq!((*fn_pp)(1u32, 2i8, 3.5f64), (2i8, 3.5f64, 1u32)); } } diff --git a/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr deleted file mode 100644 index fd9fce68eb255..0000000000000 --- a/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented - --> $DIR/splat-fn-ptr-ptr-tuple.rs:31:9 - | -LL | (*fn_pp)(1, 2); - | ^^^^^^^^^^^^^^ - - - -Box -stack backtrace: - -note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md - -note: please make sure that you have updated to the latest nightly - -note: rustc {version} running on {platform} - -query stack during panic: -#0 [thir_body] building THIR for `main` -#1 [check_unsafety] unsafety-checking `main` -#2 [analysis] running analysis passes on crate `splat_fn_ptr_ptr_tuple` -end of query stack -error: aborting due to 1 previous error - diff --git a/tests/ui/splat/splat-fn-ptr-tuple-const.rs b/tests/ui/splat/splat-fn-ptr-tuple-const.rs index 035c4db9ad5be..c95c20fc89772 100644 --- a/tests/ui/splat/splat-fn-ptr-tuple-const.rs +++ b/tests/ui/splat/splat-fn-ptr-tuple-const.rs @@ -1,17 +1,4 @@ //! Test using `#[rustc_splat]` on tuple arguments of generic function constants. -//! Currently ICEs (#158603), but if we fix it, we'll want to know and update this test to pass. - -//@ failure-status: 101 - -//@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" -//@ normalize-stderr: "thread.*panicked at .*compiler.*" -> "" -//@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" -//@ normalize-stderr: "note: compiler flags.*\n\n" -> "" -//@ normalize-stderr: " +\d{1,}: .*\n" -> "" -//@ normalize-stderr: " + at .*\n" -> "" -//@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" -//@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" -//@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" #![allow(incomplete_features)] #![feature(splat, tuple_trait)] @@ -20,15 +7,12 @@ use std::marker::Tuple; fn f(#[rustc_splat] args: Args) {} +// FIXME(rustfmt): the attribute gets deleted by rustfmt +#[rustfmt::skip] fn main() { - // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in - // MIR lowering - // FIXME(rustfmt): the attribute gets deleted by rustfmt - #[rustfmt::skip] const F2: fn(#[rustc_splat] (u8, u32)) = f::<(u8, u32)>; - const R2: () = F2(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented + const R2: () = F2(1, 2); //~ ERROR function pointer calls are not allowed in constants - #[rustfmt::skip] const F1: fn(#[rustc_splat] ((u8, u32),)) = f::<((u8, u32),)>; - const R1: () = F1((1, 2)); //~ ERROR splatted FnPtr side-tables are not yet implemented + const R1: () = F1((1, 2)); //~ ERROR function pointer calls are not allowed in constants } diff --git a/tests/ui/splat/splat-fn-ptr-tuple-const.stderr b/tests/ui/splat/splat-fn-ptr-tuple-const.stderr index 1767782a9535e..f4b033445b3b2 100644 --- a/tests/ui/splat/splat-fn-ptr-tuple-const.stderr +++ b/tests/ui/splat/splat-fn-ptr-tuple-const.stderr @@ -1,52 +1,14 @@ -error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented - --> $DIR/splat-fn-ptr-tuple-const.rs:29:20 +error: function pointer calls are not allowed in constants + --> $DIR/splat-fn-ptr-tuple-const.rs:14:20 | LL | const R2: () = F2(1, 2); | ^^^^^^^^ - - -Box -stack backtrace: - -note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md - -note: please make sure that you have updated to the latest nightly - -note: rustc {version} running on {platform} - -query stack during panic: -#0 [thir_body] building THIR for `main::R2` -#1 [check_match] match-checking `main::R2` -#2 [mir_built] building MIR for `main::R2` -#3 [trivial_const] checking if `main::R2` is a trivial const -#4 [eval_to_const_value_raw] simplifying constant for the type system `main::R2` -#5 [analysis] running analysis passes on crate `splat_fn_ptr_tuple_const` -end of query stack -error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented - --> $DIR/splat-fn-ptr-tuple-const.rs:33:20 +error: function pointer calls are not allowed in constants + --> $DIR/splat-fn-ptr-tuple-const.rs:17:20 | LL | const R1: () = F1((1, 2)); | ^^^^^^^^^^ - - -Box -stack backtrace: - -note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md - -note: please make sure that you have updated to the latest nightly - -note: rustc {version} running on {platform} - -query stack during panic: -#0 [thir_body] building THIR for `main::R1` -#1 [check_match] match-checking `main::R1` -#2 [mir_built] building MIR for `main::R1` -#3 [trivial_const] checking if `main::R1` is a trivial const -#4 [eval_to_const_value_raw] simplifying constant for the type system `main::R1` -#5 [analysis] running analysis passes on crate `splat_fn_ptr_tuple_const` -end of query stack error: aborting due to 2 previous errors diff --git a/tests/ui/splat/splat-fn-ptr-tuple-fail.rs b/tests/ui/splat/splat-fn-ptr-tuple-fail.rs new file mode 100644 index 0000000000000..a76f9f30cae32 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple-fail.rs @@ -0,0 +1,18 @@ +//! Test using `#[rustc_splat]` on tuple arguments of pointers to invalid simple functions. +//! Bug #158603 regression test +//@ run-fail +//@ check-run-results +//@ exec-env: RUST_BACKTRACE=0 + +//@ normalize-stderr: "thread '.*'" -> "thread 'NAME'" +//@ normalize-stderr: "note: run with.*\n" -> "" + +#![expect(incomplete_features)] +#![feature(splat)] + +fn main() { + // FIXME(rustfmt): the attribute gets deleted by rustfmt + #[rustfmt::skip] + let x: fn(#[rustc_splat] (i32,)) = None.unwrap(); + x(1); +} diff --git a/tests/ui/splat/splat-fn-ptr-tuple-fail.run.stderr b/tests/ui/splat/splat-fn-ptr-tuple-fail.run.stderr new file mode 100644 index 0000000000000..7536f99c69bd6 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple-fail.run.stderr @@ -0,0 +1,3 @@ + +thread 'NAME' ($TID) panicked at $DIR/splat-fn-ptr-tuple-fail.rs:16:45: +called `Option::unwrap()` on a `None` value diff --git a/tests/ui/splat/splat-fn-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-tuple.rs index 7fb06ad1c6bc1..23690865e9aaf 100644 --- a/tests/ui/splat/splat-fn-ptr-tuple.rs +++ b/tests/ui/splat/splat-fn-ptr-tuple.rs @@ -1,46 +1,43 @@ //! Test using `#[rustc_splat]` on tuple arguments of pointers to simple functions. -//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. +//! Bug #158603 regression test +//@ run-pass -//@ failure-status: 101 - -//@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" -//@ normalize-stderr: "thread.*panicked at .*compiler.*" -> "" -//@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" -//@ normalize-stderr: "note: compiler flags.*\n\n" -> "" -//@ normalize-stderr: " +\d{1,}: .*\n" -> "" -//@ normalize-stderr: " + at .*\n" -> "" -//@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" -//@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" -//@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" - -#![allow(incomplete_features)] +#![expect(incomplete_features)] #![feature(splat)] -fn tuple_args(#[rustc_splat] (_a, _b): (u32, i8)) {} +fn tuple_args(#[rustc_splat] (a, b): (u32, i8)) -> (u32, i8) { + (a, b) +} -fn splat_non_terminal_arg(#[rustc_splat] (_a, _b): (u32, i8), _c: f64) {} +fn splat_non_terminal_arg(#[rustc_splat] (a, b): (u32, i8), c: f64) -> (f64, i8, u32) { + // Permute the returned values as a codegen test + (c, b, a) +} +// FIXME(rustfmt): the attribute gets deleted by rustfmt +#[rustfmt::skip] fn main() { - // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in - // MIR lowering - // FIXME(rustfmt): the attribute gets deleted by rustfmt - #[rustfmt::skip] - let fn_ptr: fn(#[rustc_splat] (u32, i8)) = tuple_args; - fn_ptr(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented - // The ICE means that code after this line is not fully checked - fn_ptr(1u32, 2i8); - - // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? - // Add a tupled test for each call if they are. - //fn_ptr((1, 2)); // ERROR this splatted function takes 2 arguments, but 1 was provided - - #[rustfmt::skip] - let fn_ptr: fn(#[rustc_splat] (u32, i8), f64) = splat_non_terminal_arg; - fn_ptr(1, 2, 3.5); - fn_ptr(1u32, 2i8, 3.5f64); - - // Bug #158603 regression test - #[rustfmt::skip] - let x: fn(#[rustc_splat] (i32,)) = None.unwrap(); - x(1); + let fn_ptr: fn(#[rustc_splat] (u32, i8)) -> (u32, i8) + = tuple_args as fn(#[rustc_splat] (u32, i8)) -> (u32, i8); + assert_eq!(fn_ptr(1, 2), (1, 2)); + assert_eq!(fn_ptr(1u32, 2i8), (1u32, 2i8)); + + let fn_ptr = tuple_args as fn(#[rustc_splat] (u32, i8)) -> (u32, i8); + assert_eq!(fn_ptr(1, 2), (1, 2)); + assert_eq!(fn_ptr(1u32, 2i8), (1u32, 2i8)); + + let fn_ptr: fn(#[rustc_splat] (u32, i8)) -> (u32, i8) = tuple_args as _; + assert_eq!(fn_ptr(1, 2), (1, 2)); + assert_eq!(fn_ptr(1u32, 2i8), (1u32, 2i8)); + + // Now without explicit `as` + let fn_ptr: fn(#[rustc_splat] (u32, i8), f64) -> (f64, i8, u32) = splat_non_terminal_arg; + assert_eq!(fn_ptr(1, 2, 3.5), (3.5, 2, 1)); + assert_eq!(fn_ptr(1u32, 2i8, 3.5f64), (3.5f64, 2i8, 1u32)); + + // FIXME(unused_variables): This is obviously used + #[expect(unused_variables)] + let fn_ptr = splat_non_terminal_arg; + assert_eq!(fn_ptr(1, 2, 3.5), (3.5, 2, 1)); + assert_eq!(fn_ptr(1u32, 2i8, 3.5f64), (3.5f64, 2i8, 1u32)); } diff --git a/tests/ui/splat/splat-fn-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-tuple.stderr deleted file mode 100644 index 4cc861cafe968..0000000000000 --- a/tests/ui/splat/splat-fn-ptr-tuple.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented - | -LL | fn_ptr(1, 2); - | ^^^^^^^^^^^^ - - - -Box -stack backtrace: - -note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md - -note: please make sure that you have updated to the latest nightly - -note: rustc {version} running on {platform} - -query stack during panic: -#0 [thir_body] building THIR for `main` -#1 [check_unsafety] unsafety-checking `main` -#2 [analysis] running analysis passes on crate `splat_fn_ptr_tuple` -end of query stack -error: aborting due to 1 previous error - diff --git a/tests/ui/statics/extern-static-in-static-ice-143174.rs b/tests/ui/statics/extern-static-in-static-ice-143174.rs new file mode 100644 index 0000000000000..868d9bab73663 --- /dev/null +++ b/tests/ui/statics/extern-static-in-static-ice-143174.rs @@ -0,0 +1,15 @@ +// Regression test for #143174. + +#![crate_type = "lib"] + +type Fun = unsafe extern "C" fn(); + +struct Foo(Fun); + +static FOO: &Foo = &Foo(BAR); +//~^ ERROR cannot access extern static `BAR` [E0080] +//~| ERROR use of extern static is unsafe and requires unsafe function or block [E0133] + +unsafe extern "C" { + static BAR: Fun; +} diff --git a/tests/ui/statics/extern-static-in-static-ice-143174.stderr b/tests/ui/statics/extern-static-in-static-ice-143174.stderr new file mode 100644 index 0000000000000..f38968031ba66 --- /dev/null +++ b/tests/ui/statics/extern-static-in-static-ice-143174.stderr @@ -0,0 +1,18 @@ +error[E0080]: cannot access extern static `BAR` + --> $DIR/extern-static-in-static-ice-143174.rs:9:25 + | +LL | static FOO: &Foo = &Foo(BAR); + | ^^^ evaluation of `FOO` failed here + +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/extern-static-in-static-ice-143174.rs:9:25 + | +LL | static FOO: &Foo = &Foo(BAR); + | ^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0080, E0133. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/statics/extern-static-promotion-rejected.rs b/tests/ui/statics/extern-static-promotion-rejected.rs new file mode 100644 index 0000000000000..f024f00490f74 --- /dev/null +++ b/tests/ui/statics/extern-static-promotion-rejected.rs @@ -0,0 +1,11 @@ +// previously part of tests/mir-opt/const_promotion_extern_static.rs +// promotion of extern statics is now rejected entirely, even if we're not trying to read its value + +unsafe extern "C" { + static X: i32; +} + +static mut FOO: *const &i32 = [unsafe { &X }].as_ptr(); +//~^ ERROR dangling pointer + +fn main() {} diff --git a/tests/ui/statics/extern-static-promotion-rejected.stderr b/tests/ui/statics/extern-static-promotion-rejected.stderr new file mode 100644 index 0000000000000..52e24a0bd942c --- /dev/null +++ b/tests/ui/statics/extern-static-promotion-rejected.stderr @@ -0,0 +1,8 @@ +error: encountered dangling pointer in final value of mutable static + --> $DIR/extern-static-promotion-rejected.rs:8:1 + | +LL | static mut FOO: *const &i32 = [unsafe { &X }].as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/structs/ice-missing-field-fn-sig.rs b/tests/ui/structs/ice-missing-field-fn-sig.rs new file mode 100644 index 0000000000000..9ba48f6a6e795 --- /dev/null +++ b/tests/ui/structs/ice-missing-field-fn-sig.rs @@ -0,0 +1,14 @@ +// A struct literal that's missing fields shouldn't ICE when checking the fn sig. + +trait Context {} +struct Wrapper { + container: &'static C, +} +fn foobar(_: Wrapper<()>) { //~ ERROR the trait bound `(): Context` is not satisfied + foobar(Wrapper { /* missing */ }) +//~^ ERROR the trait bound `(): Context` is not satisfied +//~^^ ERROR missing field `container` in initializer of `Wrapper<_>` +//~^^^ ERROR the trait bound `(): Context` is not satisfied +} + +fn main() {} diff --git a/tests/ui/structs/ice-missing-field-fn-sig.stderr b/tests/ui/structs/ice-missing-field-fn-sig.stderr new file mode 100644 index 0000000000000..7be7886b2d975 --- /dev/null +++ b/tests/ui/structs/ice-missing-field-fn-sig.stderr @@ -0,0 +1,61 @@ +error[E0277]: the trait bound `(): Context` is not satisfied + --> $DIR/ice-missing-field-fn-sig.rs:7:14 + | +LL | fn foobar(_: Wrapper<()>) { + | ^^^^^^^^^^^ the trait `Context` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/ice-missing-field-fn-sig.rs:3:1 + | +LL | trait Context {} + | ^^^^^^^^^^^^^ +note: required by a bound in `Wrapper` + --> $DIR/ice-missing-field-fn-sig.rs:4:19 + | +LL | struct Wrapper { + | ^^^^^^^ required by this bound in `Wrapper` + +error[E0277]: the trait bound `(): Context` is not satisfied + --> $DIR/ice-missing-field-fn-sig.rs:8:12 + | +LL | foobar(Wrapper { /* missing */ }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Context` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/ice-missing-field-fn-sig.rs:3:1 + | +LL | trait Context {} + | ^^^^^^^^^^^^^ +note: required by a bound in `Wrapper` + --> $DIR/ice-missing-field-fn-sig.rs:4:19 + | +LL | struct Wrapper { + | ^^^^^^^ required by this bound in `Wrapper` + +error[E0063]: missing field `container` in initializer of `Wrapper<_>` + --> $DIR/ice-missing-field-fn-sig.rs:8:12 + | +LL | foobar(Wrapper { /* missing */ }) + | ^^^^^^^ missing `container` + +error[E0277]: the trait bound `(): Context` is not satisfied + --> $DIR/ice-missing-field-fn-sig.rs:8:12 + | +LL | foobar(Wrapper { /* missing */ }) + | ^^^^^^^ the trait `Context` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/ice-missing-field-fn-sig.rs:3:1 + | +LL | trait Context {} + | ^^^^^^^^^^^^^ +note: required by a bound in `Wrapper` + --> $DIR/ice-missing-field-fn-sig.rs:4:19 + | +LL | struct Wrapper { + | ^^^^^^^ required by this bound in `Wrapper` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0063, E0277. +For more information about an error, try `rustc --explain E0063`. diff --git a/tests/ui/suggestions/issue-102972.stderr b/tests/ui/suggestions/issue-102972.stderr index 501a5c9efd35e..c5d593090fa68 100644 --- a/tests/ui/suggestions/issue-102972.stderr +++ b/tests/ui/suggestions/issue-102972.stderr @@ -1,14 +1,18 @@ error[E0499]: cannot borrow `chars` as mutable more than once at a time --> $DIR/issue-102972.rs:6:9 | -LL | for _c in chars.by_ref() { - | -------------- - | | - | first mutable borrow occurs here - | first borrow later used here -LL | chars.next(); - | ^^^^^ second mutable borrow occurs here - | +LL | for _c in chars.by_ref() { + | - -------------- + | | | + | | first mutable borrow occurs here + | _____| first borrow later used here + | | +LL | | chars.next(); + | | ^^^^^ second mutable borrow occurs here +LL | | } + | |_____- this for loop borrows `chars` mutably, preventing mutation within its body + | + = help: consider using an index-based loop instead, or collecting modifications into a separate collection = note: a for loop advances the iterator for you, the result is stored in `_c` help: if you want to call `next` on a iterator within the loop, consider using `while let` | @@ -39,14 +43,18 @@ LL + while let Some(_i) = iter.next() { error[E0499]: cannot borrow `i` as mutable more than once at a time --> $DIR/issue-102972.rs:22:9 | -LL | for () in i.by_ref() { - | ---------- - | | - | first mutable borrow occurs here - | first borrow later used here -LL | i.next(); - | ^ second mutable borrow occurs here - | +LL | for () in i.by_ref() { + | - ---------- + | | | + | | first mutable borrow occurs here + | _____| first borrow later used here + | | +LL | | i.next(); + | | ^ second mutable borrow occurs here +LL | | } + | |_____- this for loop borrows `i` mutably, preventing mutation within its body + | + = help: consider using an index-based loop instead, or collecting modifications into a separate collection = note: a for loop advances the iterator for you, the result is stored in its pattern help: if you want to call `next` on a iterator within the loop, consider using `while let` |