diff --git a/Cargo.lock b/Cargo.lock index 2c9dc442663f8..2d9b5d6f84665 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3634,6 +3634,7 @@ version = "0.0.0" dependencies = [ "rustc_abi", "rustc_ast", + "rustc_attr_ir", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -4068,6 +4069,7 @@ name = "rustc_feature" version = "0.0.0" dependencies = [ "rustc_data_structures", + "rustc_macros", "rustc_span", "serde", "serde_json", diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index f7128e66193a8..9dc5f81581e87 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -10,6 +10,7 @@ doctest = false # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index db0dd2fcc6191..4d5b98fd1ac00 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -3,19 +3,19 @@ use std::ops::ControlFlow; use std::sync::Arc; use rustc_ast::node_id::NodeMap; +use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::*; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::target::Target; use rustc_errors::msg; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_hir::HirId; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{HirId, Target, find_attr}; use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; use rustc_session::diagnostics::report_lit_error; use rustc_span::{ByteSymbol, DUMMY_SP, DesugaringKind, Ident, Span, Spanned, Symbol, respan, sym}; use thin_vec::{ThinVec, thin_vec}; -use visit::{Visitor, walk_expr}; - mod closure; use crate::diagnostics::{ @@ -882,35 +882,17 @@ impl<'hir> LoweringContext<'_, 'hir> { /// Forwards a possible `#[track_caller]` annotation from `outer_hir_id` to /// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled. - pub(super) fn maybe_forward_track_caller( - &mut self, - span: Span, - outer_hir_id: HirId, - inner_hir_id: HirId, - ) { + pub(super) fn maybe_forward_track_caller(&mut self, outer_hir_id: HirId, inner_hir_id: HirId) { if self.tcx.features().async_fn_track_caller() && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id) - && find_attr!(*attrs, TrackCaller(_)) + && let Some(t) = attrs.iter().find(|a| { + matches!( + a, + rustc_attr_ir::Attribute::Parsed(rustc_attr_ir::AttributeKind::TrackCaller(_)) + ) + }) { - let unstable_span = self.mark_span_with_reason( - DesugaringKind::Async, - span, - Some(Arc::clone(&self.allow_gen_future)), - ); - self.lower_attrs( - inner_hir_id, - &[Attribute { - kind: AttrKind::Normal(Box::new(NormalAttr::from_ident(Ident::new( - sym::track_caller, - span, - )))), - id: self.tcx.sess.psess.attr_id_generator.mk_attr_id(), - style: AttrStyle::Outer, - span: unstable_span, - }], - span, - Target::Fn, - ); + self.attrs.insert(inner_hir_id.local_id, std::slice::from_ref(t)); } } diff --git a/compiler/rustc_ast_lowering/src/expr/closure.rs b/compiler/rustc_ast_lowering/src/expr/closure.rs index 8c5c55e07fb04..2831fb4fa8352 100644 --- a/compiler/rustc_ast_lowering/src/expr/closure.rs +++ b/compiler/rustc_ast_lowering/src/expr/closure.rs @@ -343,7 +343,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }); - this.maybe_forward_track_caller(body.span, closure_hir_id, expr.hir_id); + this.maybe_forward_track_caller(closure_hir_id, expr.hir_id); (parameters, expr) }); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index d5ef2f9e832dd..1ad96d1057042 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1462,7 +1462,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // FIXME(async_fn_track_caller): Can this be moved above? let hir_id = expr.hir_id; - this.maybe_forward_track_caller(body.span, fn_id, hir_id); + this.maybe_forward_track_caller(fn_id, hir_id); (parameters, expr) }) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index d7f2243faaab9..e9ace7088d8f0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -436,7 +436,7 @@ fn parse_cfg_attr_internal<'a>( } fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) { - let gate = find_gated_cfg(|sym| sym == name); + let gate = find_gated_cfg(name); if let (Some(feats), Some(gated_cfg)) = (features, gate) { gate_cfg(gated_cfg, span, sess, feats); } diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 8905cd704c6c4..bff7d7ad81cb9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -364,6 +364,15 @@ impl NoArgsAttributeParser for TrackCallerParser { }); } } + Target::Closure if !cx.features().closure_track_caller() => { + feature_err( + cx.sess(), + sym::closure_track_caller, + attr_span, + "`#[track_caller]` on closures is currently unstable", + ) + .emit(); + } _ => {} } } diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index e315d6abea395..6cce64d700a63 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -43,7 +43,7 @@ fn check_keyword(cx: &mut AcceptContext<'_, '_>, keyword: Symbol, span: Span) -> fn check_attribute(cx: &mut AcceptContext<'_, '_>, attribute: Symbol, span: Span) -> bool { // FIXME: This should support attributes with namespace like `diagnostic::do_not_recommend`. - if rustc_feature::BUILTIN_ATTRIBUTE_MAP.contains(&attribute) { + if rustc_feature::BUILTIN_ATTRIBUTE_SET.contains(&attribute) { return true; } cx.emit_err(DocAttributeNotAttribute { span, attribute }); diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index aef7dd48ec664..240f437828259 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -10,7 +10,7 @@ use rustc_attr_ir::target::Target; use rustc_attr_ir::{AttrArgs, AttrItem, AttrPath, Attribute, AttributeKind, HashIgnoredAttrId}; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; -use rustc_feature::{BUILTIN_ATTRIBUTE_MAP, Features}; +use rustc_feature::{BUILTIN_ATTRIBUTE_SET, Features}; use rustc_lint_defs::{LintId, RegisteredTools}; use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; @@ -376,7 +376,7 @@ impl<'sess> AttributeParser<'sess> { ); self.check_attribute_stability(&attr_path, attr_span, accept.stability); if let [part] = parts.as_slice() { - debug_assert!(BUILTIN_ATTRIBUTE_MAP.contains(part)); + debug_assert!(BUILTIN_ATTRIBUTE_SET.contains(part)); } let Some(args) = ArgParser::from_attr_args( diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index f225458ebc0e6..4719ee5103877 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -11,7 +11,7 @@ use rustc_ast::{ }; use rustc_attr_ir::AttrPath; use rustc_errors::{Applicability, Diagnostic, PResult}; -use rustc_feature::BUILTIN_ATTRIBUTE_MAP; +use rustc_feature::BUILTIN_ATTRIBUTE_SET; use rustc_lint_defs::builtin::ILL_FORMED_ATTRIBUTE_INPUT; use rustc_parse::parse_in; use rustc_session::diagnostics::report_lit_error; @@ -27,7 +27,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) | AttrKind::DocComment(..) => return, } - let builtin_attr_info = attr.name().and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name)); + let builtin_attr_info = attr.name().and_then(|name| BUILTIN_ATTRIBUTE_SET.get(&name)); // Check input tokens for built-in and key-value attributes. if let Some(name) = builtin_attr_info { diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 672b58fcbfbea..f4a1edb0b675d 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -126,7 +126,7 @@ pub(crate) fn compute_regions<'tcx>( let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output()) || infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled(); - let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints( + let mut lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints( constraints, &universal_region_relations, infcx, @@ -144,6 +144,20 @@ pub(crate) fn compute_regions<'tcx>( &lowered_constraints, ); + // If requested for `-Zpolonius=next`, compute loan liveness information. + // This is done prior to `RegionInferenceContext::new`, because we may add + // additional liveness constraints. + if let Some(polonius_context) = polonius_context.as_mut() { + let _timer = infcx.tcx.prof.generic_activity("borrowck_polonius_loan_liveness"); + polonius_context.compute_loan_liveness( + &mut lowered_constraints.liveness_constraints, + lowered_constraints.outlives_constraints.outlives().iter().copied(), + &universal_region_relations.universal_regions, + body, + borrow_set, + ); + } + let mut regioncx = RegionInferenceContext::new( infcx, lowered_constraints, @@ -151,13 +165,6 @@ pub(crate) fn compute_regions<'tcx>( location_map, ); - // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints - // and use them to compute loan liveness. - if let Some(polonius_context) = polonius_context.as_mut() { - let _timer = infcx.tcx.prof.generic_activity("borrowck_polonius_loan_liveness"); - polonius_context.compute_loan_liveness(&mut regioncx, body, borrow_set) - } - // If requested: dump NLL facts, and run legacy polonius analysis. let polonius_output = polonius_facts.as_ref().and_then(|polonius_facts| { if infcx.tcx.sess.opts.unstable_opts.nll_facts { diff --git a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs index b6f8b4a79f39b..4009a85180571 100644 --- a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs @@ -6,25 +6,23 @@ use rustc_middle::ty::relate::{ }; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable}; -use super::{ConstraintDirection, PoloniusContext}; +use super::ConstraintDirection; use crate::universal_regions::UniversalRegions; -impl PoloniusContext { - /// Record the variance of each region contained within the given value. - pub(crate) fn record_live_region_variance<'tcx>( - &mut self, - tcx: TyCtxt<'tcx>, - universal_regions: &UniversalRegions<'tcx>, - value: impl TypeVisitable> + Relate>, - ) { - let mut extractor = VarianceExtractor { - tcx, - ambient_variance: ty::Variance::Covariant, - directions: &mut self.live_region_variances, - universal_regions, - }; - extractor.relate(value, value).expect("Can't have a type error relating to itself"); - } +/// Record the variance of each region contained within the given value. +pub(crate) fn record_live_region_variance<'tcx>( + tcx: TyCtxt<'tcx>, + live_region_variances: &mut BTreeMap, + universal_regions: &UniversalRegions<'tcx>, + value: impl TypeVisitable> + Relate>, +) { + let mut extractor = VarianceExtractor { + tcx, + ambient_variance: ty::Variance::Covariant, + directions: live_region_variances, + universal_regions, + }; + extractor.relate(value, value).expect("Can't have a type error relating to itself"); } /// Extracts variances for regions contained within types. Follows the same structure as diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 45108bfcb79ba..cbac05d2eff67 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -48,9 +48,12 @@ use rustc_mir_dataflow::points::PointIndex; pub(self) use self::constraints::*; pub(crate) use self::dump::dump_polonius_mir; +pub(crate) use self::liveness_constraints::record_live_region_variance; +use crate::BorrowSet; +use crate::constraints::OutlivesConstraint; use crate::dataflow::BorrowIndex; use crate::region_infer::values::LivenessValues; -use crate::{BorrowSet, RegionInferenceContext}; +use crate::universal_regions::UniversalRegions; pub(crate) type LiveLoans = SparseBitMatrix; @@ -65,7 +68,7 @@ pub(crate) struct PoloniusContext { /// The expected edge direction per live region: the kind of directed edge we'll create as /// liveness constraints depends on the variance of types with respect to each contained region. - live_region_variances: BTreeMap, + pub(crate) live_region_variances: BTreeMap, /// The regions that outlive free regions are used to distinguish relevant live locals from /// boring locals. A boring local is one whose type contains only such regions. Polonius @@ -77,7 +80,7 @@ pub(crate) struct PoloniusContext { /// The direction a constraint can flow into. Used to create liveness constraints according to /// variance. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ConstraintDirection { +pub(crate) enum ConstraintDirection { /// For covariant cases, we add a forward edge `O at P1 -> O at P2`. Forward, @@ -101,19 +104,19 @@ impl PoloniusContext { /// The constraint data will be used to compute errors and diagnostics. pub(crate) fn compute_loan_liveness<'tcx>( &mut self, - regioncx: &mut RegionInferenceContext<'tcx>, + liveness: &mut LivenessValues, + outlives_constraints: impl Iterator>, + universal_regions: &UniversalRegions<'tcx>, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, ) { - let liveness = regioncx.liveness_constraints(); - // We don't need to prepare the graph (index NLL constraints, etc.) if we have no loans to // trace throughout localized constraints. if borrow_set.len() > 0 { // From the outlives constraints, liveness, and variances, we can compute reachability // on the lazy localized constraint graph to trace the liveness of loans, for the next // step in the chain (the NLL loan scope and active loans computations). - let graph = LocalizedConstraintGraph::new(liveness, regioncx.outlives_constraints()); + let graph = LocalizedConstraintGraph::new(liveness, outlives_constraints); let mut live_loans = LiveLoans::new(borrow_set.len()); let mut visitor = LoanLivenessVisitor { liveness, live_loans: &mut live_loans }; @@ -121,11 +124,11 @@ impl PoloniusContext { body, liveness, &self.live_region_variances, - regioncx.universal_regions(), + universal_regions, borrow_set, &mut visitor, ); - regioncx.record_live_loans(live_loans); + liveness.record_live_loans(live_loans); // The graph can be traversed again during MIR dumping, so we store it here. self.graph = Some(graph); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 19aa4081bfc88..d3fc7152acc44 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -30,7 +30,6 @@ use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstra use crate::dataflow::BorrowIndex; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; -use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues}; use crate::type_check::Locations; @@ -1874,12 +1873,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self.liveness_constraints } - /// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active - /// loans dataflow computations. - pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) { - self.liveness_constraints.record_live_loans(live_loans); - } - /// Returns whether the `loan_idx` is live at the given `location`: whether its issuing /// region is contained within the type of a variable that is live at this point. /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`. diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index fd8502773c51e..dfab2fd071773 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -11,7 +11,7 @@ use tracing::debug; use super::TypeChecker; use crate::constraints::OutlivesConstraintSet; -use crate::polonius::PoloniusContext; +use crate::polonius::{PoloniusContext, record_live_region_variance}; use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; @@ -67,7 +67,7 @@ pub(super) fn generate<'tcx>( let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); - trace::trace(typeck, location_map, move_data, relevant_live_locals, boring_locals); + trace::trace(typeck, location_map, move_data, &relevant_live_locals, &boring_locals); // Mark regions that should be live where they appear within rvalues or within a call: like // args, regions, and types. @@ -220,7 +220,12 @@ impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> { // When using `-Zpolonius=next`, we record the variance of each live region. if let Some(polonius_context) = self.polonius_context { - polonius_context.record_live_region_variance(self.tcx, self.universal_regions, value); + record_live_region_variance( + self.tcx, + &mut polonius_context.live_region_variances, + self.universal_regions, + value, + ); } } } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index fe20bb6c28c0c..89a8899a991c9 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -3,7 +3,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::traits::TraitErrors; -use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, HasLocalDecls, Local, Location}; +use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location}; use rustc_middle::traits::query::DropckOutlivesResult; use rustc_middle::ty::relate::Relate; use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; @@ -19,7 +19,8 @@ use rustc_trait_selection::traits::query::dropck_outlives; use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOpOutput}; use tracing::debug; -use crate::polonius; +use crate::BorrowckInferCtxt; +use crate::polonius::{self, record_live_region_variance}; use crate::region_infer::values; use crate::type_check::liveness::local_use_map::LocalUseMap; use crate::type_check::{NormalizeLocation, TypeChecker}; @@ -42,8 +43,8 @@ pub(super) fn trace<'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, location_map: &DenseLocationMap, move_data: &MoveData<'tcx>, - relevant_live_locals: Vec, - boring_locals: Vec, + relevant_live_locals: &[Local], + boring_locals: &[Local], ) { let _timer = typeck.tcx().prof.generic_activity("borrowck_liveness_trace"); @@ -59,7 +60,7 @@ pub(super) fn trace<'tcx>( let mut results = LivenessResults::new(cx); - results.add_extra_drop_facts(&relevant_live_locals); + results.add_extra_drop_facts(relevant_live_locals); results.compute_for_all_locals(relevant_live_locals); @@ -131,8 +132,8 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { } } - fn compute_for_all_locals(&mut self, relevant_live_locals: Vec) { - for local in relevant_live_locals { + fn compute_for_all_locals(&mut self, relevant_live_locals: &[Local]) { + for &local in relevant_live_locals { self.reset_local_state(); self.add_defs_for(local); self.compute_use_live_points_for(local); @@ -161,20 +162,11 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { /// These are all the locals which do not potentially reference a region local /// to this body. Locals which only reference free regions are always drop-live /// and can therefore safely be dropped. - fn dropck_boring_locals(&mut self, boring_locals: Vec) { - for local in boring_locals { + fn dropck_boring_locals(&mut self, boring_locals: &[Local]) { + for &local in boring_locals { let local_ty = self.cx.body().local_decls[local].ty; let local_span = self.cx.body().local_decls[local].source_info.span; - let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({ - let typeck = &self.cx.typeck; - move || LivenessContext::compute_drop_data(typeck, local_ty, local_span) - }); - - drop_data.dropck_result.report_overflows( - self.cx.typeck.infcx.tcx, - self.cx.typeck.body.local_decls[local].source_info.span, - local_ty, - ); + dropck_local(&self.cx.typeck.infcx, &mut self.cx.drop_data, local_ty, local_span); } } @@ -567,11 +559,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { values::pretty_print_points(self.location_map, live_at.iter()), ); - let local_span = self.body().local_decls()[dropped_local].source_info.span; - let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ - let typeck = &self.typeck; - move || Self::compute_drop_data(typeck, dropped_ty, local_span) - }); + let dropped_span = self.body().local_decls[dropped_local].source_info.span; + let drop_data = + dropck_local(&self.typeck.infcx, &mut self.drop_data, dropped_ty, dropped_span); if let Some(data) = &drop_data.region_constraint_data { for &drop_location in drop_locations { @@ -583,12 +573,6 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { } } - drop_data.dropck_result.report_overflows( - self.typeck.infcx.tcx, - self.typeck.body.source_info(*drop_locations.first().unwrap()).span, - dropped_ty, - ); - // All things in the `outlives` array may be touched by // the destructor and must be live at this point. for &kind in &drop_data.dropck_result.kinds { @@ -627,24 +611,27 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { // When using `-Zpolonius=next`, we record the variance of each live region. if let Some(polonius_context) = typeck.polonius_context.as_mut() { - polonius_context.record_live_region_variance( + record_live_region_variance( typeck.infcx.tcx, + &mut polonius_context.live_region_variances, typeck.universal_regions, value, ); } } +} - fn compute_drop_data( - typeck: &TypeChecker<'_, 'tcx>, - dropped_ty: Ty<'tcx>, - span: Span, - ) -> DropData<'tcx> { - debug!("compute_drop_data(dropped_ty={:?})", dropped_ty); - - let goal = DropckOutlives { dropped_ty }; - - match typeck.infcx.fully_perform(goal, DUMMY_SP) { +/// Computes the `DropData` for a given type, caching the result. +/// This also reports the overflow errors from the computation, if any. +fn dropck_local<'tcx, 'd>( + infcx: &BorrowckInferCtxt<'tcx>, + drop_data: &'d mut FxIndexMap, DropData<'tcx>>, + local_ty: Ty<'tcx>, + local_span: Span, +) -> &'d DropData<'tcx> { + let compute_drop_data = || { + let goal = DropckOutlives { dropped_ty: local_ty }; + match infcx.fully_perform(goal, DUMMY_SP) { Ok(TypeOpOutput { output, constraints, .. }) => { DropData { dropck_result: output, region_constraint_data: constraints } } @@ -656,12 +643,12 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { // // Do this inside of a probe because we don't particularly care (or want) // any region side-effects of this operation in our infcx. - typeck.infcx.probe(|_| { - let ocx = ObligationCtxt::new_with_diagnostics(&typeck.infcx); + infcx.probe(|_| { + let ocx = ObligationCtxt::new_with_diagnostics(infcx); let errors = match dropck_outlives::compute_dropck_outlives_with_errors( &ocx, - typeck.infcx.param_env.and(goal), - span, + infcx.param_env.and(goal), + local_span, ) { Ok(_) => ocx.evaluate_obligations_error_on_ambiguity(), Err(e) => TraitErrors::HasErrors(e), @@ -670,11 +657,15 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { // Could have no errors if a type lowering error, say, caused the query // to fail. if let TraitErrors::HasErrors(errors) = errors { - typeck.infcx.err_ctxt().report_fulfillment_errors(errors); + infcx.err_ctxt().report_fulfillment_errors(errors); } }); DropData { dropck_result: Default::default(), region_constraint_data: None } } } - } + }; + + let drop_data = drop_data.entry(local_ty).or_insert_with(compute_drop_data); + drop_data.dropck_result.report_overflows(infcx.tcx, local_span, local_ty); + drop_data } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index f39e3f386a54f..9f23a0d5ab631 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1769,7 +1769,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Alias(_, alias_const) => match alias_const.kind { ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => Some(UnevaluatedConst { def: def_id, diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 9763b0c0fa867..c5b164b8e9cce 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -30,6 +30,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> { operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, _line_spans: &[Span], + _extra_rust_target_features: &[String], ) { codegen_global_asm_inner(self.tcx, self.global_asm, template, operands, options); } diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index a485261cc6c23..5b17b4f83fea2 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -929,6 +929,7 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, line_spans: &[Span], + _extra_rust_target_features: &[String], ) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 6099e25df4b3e..0f74f5e81d684 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -414,6 +414,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, _line_spans: &[Span], + extra_rust_target_features: &[String], ) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); @@ -499,14 +500,26 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { template_str.push_str("\n.att_syntax\n"); } - let target_features = self.tcx.global_backend_features(()).join(","); - let target_cpu = llvm_util::target_cpu(self.tcx.sess); + // Globally-enabled features that are already in the backend format. + let global_features = self.tcx.global_backend_features(()).iter().map(String::as_str); + + // Features enabled on a particular instance, in the rust format. + // These need to be translated to the LLVM format. + let function_features: Vec<_> = extra_rust_target_features + .iter() + .flat_map(|feat| llvm_util::to_llvm_features(self.tcx.sess, feat)) + .flat_map(|feat| feat.into_iter().map(|f| format!("+{f}"))) + .collect(); + + let function_features = function_features.iter().map(String::as_str); + let target_features = + global_features.chain(function_features).intersperse(",").collect::(); llvm::append_module_inline_asm( self.llmod, template_str.as_bytes(), &target_features, - target_cpu, + llvm_util::target_cpu(self.tcx.sess), ); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 9eb4fd510fd7f..c870d1694d068 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -490,7 +490,7 @@ where }) .collect(); - cx.codegen_global_asm(asm.template, &operands, asm.options, asm.line_spans); + cx.codegen_global_asm(asm.template, &operands, asm.options, asm.line_spans, &[]); } else { span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index aae300d2f9ed5..b753ff25b1b5b 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -15,8 +15,7 @@ use rustc_middle::middle::codegen_fn_attrs::{ use rustc_middle::mono::Visibility; use rustc_middle::query::Providers; use rustc_middle::ty::{self as ty, TyCtxt}; -use rustc_session::diagnostics::feature_err; -use rustc_span::{Span, sym}; +use rustc_span::Span; use rustc_target::spec::Os; use crate::diagnostics; @@ -155,18 +154,6 @@ fn process_builtin_attrs( // This error is already reported in `rustc_ast_passes/src/ast_validation.rs`. tcx.dcx().delayed_bug("`#[track_caller]` requires the Rust ABI"); } - if is_closure - && !tcx.features().closure_track_caller() - && !attr_span.allows_unstable(sym::closure_track_caller) - { - feature_err( - &tcx.sess, - sym::closure_track_caller, - *attr_span, - "`#[track_caller]` on closures is currently unstable", - ) - .emit(); - } codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER } AttributeKind::Used { used_by } => match used_by { diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 05b87bb6d7159..939e5395e4741 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -54,7 +54,9 @@ pub fn codegen_naked_asm< template_vec.extend(template.iter().cloned()); template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end.into())); - cx.codegen_global_asm(&template_vec, &operands, options, line_spans); + let target_features: Vec<_> = + cx.tcx().asm_target_features(instance.def_id()).iter().map(|s| s.to_string()).collect(); + cx.codegen_global_asm(&template_vec, &operands, options, line_spans, &target_features); } fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>>>( diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index 85a2fe09ba414..1deed8c4dc016 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -66,12 +66,17 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes { } pub trait AsmCodegenMethods<'tcx> { + /// Codegen a module-level assembly block. + /// + /// NOTE: the target features must be the rust target feature names, not backend target + /// feature names. This argument is used to forward target features on naked functions. fn codegen_global_asm( &mut self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, line_spans: &[Span], + extra_rust_target_features: &[String], ); /// The mangled name of this instance diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 54a1babbaae72..b2a2d3dbd60dd 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -741,9 +741,7 @@ fn print_crate_info( .iter() .filter_map(|&(name, value)| { // On stable, exclude unstable flags. - if !sess.is_nightly_build() - && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some() - { + if !sess.is_nightly_build() && find_gated_cfg(name).is_some() { return None; } diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml index 454fa20032aca..093e6dd91d11b 100644 --- a/compiler/rustc_feature/Cargo.toml +++ b/compiler/rustc_feature/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start rustc_data_structures = { path = "../rustc_data_structures" } +rustc_macros = { path = "../rustc_macros" } rustc_span = { path = "../rustc_span" } serde = { version = "1.0.125", features = ["derive"] } serde_json = "1.0.59" diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index a6e6f4f78323c..37a3594e374ea 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -278,7 +278,7 @@ declare_features! ( /// Allows some increased flexibility in the name resolution rules, /// especially around globs and shadowing (RFC 1560). (accepted, item_like_imports, "1.15.0", Some(35120)), - // Allows using the `kl` and `widekl` target features and the associated intrinsics + /// Allows using the `kl` and `widekl` target features and the associated intrinsics (accepted, keylocker_x86, "1.89.0", Some(134813)), /// Allows `'a: { break 'a; }`. (accepted, label_break_value, "1.65.0", Some(48594)), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 92f77e76e83b3..cb900339faaa4 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -54,20 +54,22 @@ const GATED_CFGS: &[GatedCfg] = &[ (sym::target_object_format, sym::cfg_target_object_format, Features::cfg_target_object_format), ]; -/// Find a gated cfg determined by the `pred`icate which is given the cfg's name. -pub fn find_gated_cfg(pred: impl Fn(Symbol) -> bool) -> Option<&'static GatedCfg> { - GATED_CFGS.iter().find(|(cfg_sym, ..)| pred(*cfg_sym)) +/// Find a gated cfg matching `name`. +pub fn find_gated_cfg(name: Symbol) -> Option<&'static GatedCfg> { + GATED_CFGS.iter().find(|(cfg_sym, ..)| name == *cfg_sym) } #[derive(Clone, Debug, Copy)] pub enum AttributeStability { - /// An attribute that is unstable behind a specified feature fagte + /// An attribute that is unstable behind a specified feature gate. Unstable { /// The feature gate, for example `rustc_attrs` for rustc_* attributes. gate_name: Symbol, - /// Check function to be called during the `PostExpansionVisitor` pass, which will be one of the `Features::*` functions - gate_check: fn(&Features) -> bool, - /// Notes to be displayed when an attempt is made to use the attribute without its feature gate. + /// Check function to be called during the `PostExpansionVisitor` pass, which will be one + /// of the `Features::*` functions + gate_check: GateFn, + /// Notes to be displayed when an attempt is made to use the attribute without its feature + /// gate. notes: &'static [&'static str], }, /// A stable attribute, can be used on all release channels @@ -416,15 +418,15 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ ]; pub fn is_builtin_attr_name(name: Symbol) -> bool { - BUILTIN_ATTRIBUTE_MAP.get(&name).is_some() + BUILTIN_ATTRIBUTE_SET.contains(&name) } -pub static BUILTIN_ATTRIBUTE_MAP: LazyLock> = LazyLock::new(|| { - let mut map = FxHashSet::default(); +pub static BUILTIN_ATTRIBUTE_SET: LazyLock> = LazyLock::new(|| { + let mut set = FxHashSet::default(); for attr in BUILTIN_ATTRIBUTES.iter() { - if !map.insert(*attr) { + if !set.insert(*attr) { panic!("duplicate builtin attribute `{}`", attr); } } - map + set }); diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 859b2025619e4..5b9b899fc463f 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -70,8 +70,7 @@ impl UnstableFeatures { let is_unstable_crate = |var: &str| krate.is_some_and(|name| var.split(',').any(|new_krate| new_krate == name)); - let bootstrap = env_var_rustc_bootstrap.ok(); - if let Some(val) = bootstrap.as_deref() { + if let Ok(val) = env_var_rustc_bootstrap.as_deref() { match val { val if val == "1" || is_unstable_crate(val) => return UnstableFeatures::Cheat, // Hypnotize ourselves so that we think we are a stable compiler and thus don't @@ -129,7 +128,7 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option { - Some(NonZeroU32::new($val).unwrap()) + Some(>::new($val).unwrap()) }; } @@ -34,7 +34,7 @@ macro_rules! declare_features { issue: to_nonzero($issue), }, reason: $reason, - pull: opt_nonzero_u32!($($pull)?), + pull: opt_nonzero_u32!($($pull)?), }),+ ]; }; @@ -211,7 +211,7 @@ declare_features! ( (removed, no_coverage, "1.74.0", Some(84605), Some("renamed to `coverage_attribute`"), 114656), /// Allows `#[no_debug]`. (removed, no_debug, "1.43.0", Some(29721), Some("removed due to lack of demand"), 69667), - // Allows the use of `no_sanitize` attribute. + /// Allows the use of `no_sanitize` attribute. /// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]` (removed, no_sanitize, "1.91.0", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681), /// Note: this feature was previously recorded in a separate @@ -266,7 +266,7 @@ declare_features! ( (removed, pushpop_unsafe, "1.2.0", None, None), (removed, quad_precision_float, "1.0.0", None, None), (removed, quote, "1.33.0", Some(29601), None), - (removed, ref_pat_everywhere, "1.80.0", Some(123076), Some("superseded by `ref_pat_eat_one_layer_2024"), 125168), + (removed, ref_pat_everywhere, "1.80.0", Some(123076), Some("superseded by `ref_pat_eat_one_layer_2024`"), 125168), (removed, reflect, "1.0.0", Some(27749), None), /// Allows using the `#[register_attr]` attribute. (removed, register_attr, "1.65.0", Some(66080), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 187ce8d639fb4..ca70389815141 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -5,7 +5,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use rustc_data_structures::AtomicRef; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; +use rustc_macros::StableHash; use rustc_span::{Span, Symbol, sym}; use super::{Feature, to_nonzero}; @@ -43,18 +43,19 @@ macro_rules! status_to_enum { /// /// The former is preferred. `enabled` should only be used when the feature symbol is not a /// constant, e.g. a parameter, or when the feature is a library feature. -#[derive(Clone, Default, Debug)] +#[derive(Clone, Default, Debug, StableHash)] pub struct Features { /// `#![feature]` attrs for language features, for error reporting. enabled_lang_features: Vec, /// `#![feature]` attrs for non-language (library) features. enabled_lib_features: Vec, /// `enabled_lang_features` + `enabled_lib_features`. + #[stable_hash(ignore)] // Ignored because it's the sum of the other two fields enabled_features: FxHashSet, } /// Information about an enabled language feature. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, StableHash)] pub struct EnabledLangFeature { /// Name of the feature gate guarding the language feature. pub gate_name: Symbol, @@ -65,7 +66,7 @@ pub struct EnabledLangFeature { } /// Information about an enabled library feature. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, StableHash)] pub struct EnabledLibFeature { pub gate_name: Symbol, pub attr_sp: Span, @@ -87,11 +88,11 @@ impl Features { /// - Feature gate name. /// - The span of the `#[feature]` attribute. /// - For stable language features, version info for when it was stabilized. - pub fn enabled_lang_features(&self) -> &Vec { + pub fn enabled_lang_features(&self) -> &[EnabledLangFeature] { &self.enabled_lang_features } - pub fn enabled_lib_features(&self) -> &Vec { + pub fn enabled_lib_features(&self) -> &[EnabledLibFeature] { &self.enabled_lib_features } @@ -99,7 +100,7 @@ impl Features { &self.enabled_features } - /// Returns a iterator of enabled features in stable order. + /// Returns an iterator of enabled features in stable order. pub fn enabled_features_iter_stable_order( &self, ) -> impl Iterator + Clone { @@ -120,32 +121,6 @@ impl Features { } } -impl StableHash for Features { - fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { - // `enabled_features` is skipped because it's the sum of the lang and lib features. - let Features { enabled_lang_features, enabled_lib_features, enabled_features: _ } = self; - enabled_lang_features.stable_hash(hcx, hasher); - enabled_lib_features.stable_hash(hcx, hasher); - } -} - -impl StableHash for EnabledLangFeature { - fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { - let EnabledLangFeature { gate_name, attr_sp, stable_since } = self; - gate_name.stable_hash(hcx, hasher); - attr_sp.stable_hash(hcx, hasher); - stable_since.stable_hash(hcx, hasher); - } -} - -impl StableHash for EnabledLibFeature { - fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { - let EnabledLibFeature { gate_name, attr_sp } = self; - gate_name.stable_hash(hcx, hasher); - attr_sp.stable_hash(hcx, hasher); - } -} - macro_rules! declare_features { ($( $(#[doc = $doc:tt])* ($status:ident, $feature:ident, $ver:expr, $issue:expr), @@ -515,7 +490,7 @@ declare_features! ( (unstable, diagnostic_on_unknown, "1.96.0", Some(152900)), /// Allows macros to customize macro argument matcher diagnostics. (unstable, diagnostic_on_unmatched_args, "1.97.0", Some(155642)), - // Used by macros to not show their bodies in error messages. No-op with `-Z macro-backtrace`. + /// Used by macros to not show their bodies in error messages. No-op with `-Z macro-backtrace`. (unstable, diagnostic_opaque, "1.99.0", Some(158813)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), @@ -578,7 +553,8 @@ declare_features! ( (incomplete, generic_const_parameter_types, "1.87.0", Some(137626)), /// Allows any generic constants being used as pattern type range ends (incomplete, generic_pattern_types, "1.86.0", Some(136574)), - /// Allows registering static items globally, possibly across crates, to iterate over at runtime. + /// Allows registering static items globally, possibly across crates, to iterate over at + /// runtime. (unstable, global_registration, "1.80.0", Some(125119)), /// Allows using guards in patterns. (incomplete, guard_patterns, "1.85.0", Some(129967)), @@ -679,7 +655,7 @@ declare_features! ( (unstable, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554)), /// Allows `for` binders in where-clauses (incomplete, non_lifetime_binders, "1.69.0", Some(108185)), - /// Target feaures on nvptx. + /// Target features on nvptx. (unstable, nvptx_target_feature, "1.91.0", Some(150254)), /// Allows using enums in offset_of! (unstable, offset_of_enum, "1.75.0", Some(120141)), @@ -701,10 +677,12 @@ declare_features! ( (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows the use of raw-dylibs on ELF platforms (incomplete, raw_dylib_elf, "1.87.0", Some(135694)), + /// Allows the `Reborrow` and `CoerceShared` traits. (unstable, reborrow, "1.91.0", Some(145612)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024. (incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)), - /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant + /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural + /// variant. (incomplete, ref_pat_eat_one_layer_2024_structural, "1.81.0", Some(123076)), /// Allows using the `#[register_tool]` attribute. (unstable, register_tool, "1.41.0", Some(66079)), @@ -791,6 +769,7 @@ declare_features! ( (unstable, xtensa_target_feature, "1.98.0", Some(157063)), /// Allows `do yeet` expressions (unstable, yeet_expr, "1.62.0", Some(96373)), + /// Allows the `yield` keyword for coroutines/generators. (unstable, yield_expr, "1.87.0", Some(43122)), // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 7ad57107eebbd..e5d26cf72f9a5 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -2727,9 +2727,9 @@ fn param_env_with_gat_bounds<'tcx>( _ => clauses.push( ty::Binder::bind_with_vars( ty::ProjectionClause { - projection_term: ty::AliasTerm::new_from_def_id( + projection_term: ty::AliasTerm::new( tcx, - trait_ty.def_id, + ty::AliasTermKind::ProjectionTy { def_id: trait_ty.def_id }, rebased_args, ), term: normalize_impl_ty.into(), diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index c12bafd9d5d57..9fde34f473205 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -477,7 +477,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); debug!(?alias_args); - ty::AliasTerm::new_from_def_id(tcx, assoc_item.def_id, alias_args) + ty::AliasTerm::new_from_def_id( + tcx, + assoc_item.def_id, + alias_args, + ty::AliasConstInherentArgsKind::WithSelf, + ) }) }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index dc108c41cf787..8c22506a8b918 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -485,6 +485,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx, assoc_item.def_id, alias_args, + ty::AliasConstInherentArgsKind::WithSelf, ) }); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 16a622da61c2b..cfff8d1768f0e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1609,7 +1609,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } - Ok(TypeRelativePath::AssocItem(ty::AliasTerm::new_from_def_id(tcx, item_def_id, args))) + Ok(TypeRelativePath::AssocItem(ty::AliasTerm::new_from_def_id( + tcx, + item_def_id, + args, + ty::AliasConstInherentArgsKind::WithSelf, + ))) } /// Resolve a [type-relative](hir::QPath::TypeRelative) (and type-level) path. @@ -1773,12 +1778,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let kind = match assoc_tag { ty::AssocTag::Type => ty::AliasTermKind::InherentTy { def_id: assoc_item }, - ty::AssocTag::Const => { - // FIXME(mgca): drop once `InherentConst` accepts IAC-shaped args (issue #156181) - // without this, `new_from_args` errors (#155341). - self.require_type_const_attribute(assoc_item, span)?; - ty::AliasTermKind::InherentConst { def_id: assoc_item } - } + ty::AssocTag::Const => ty::AliasTermKind::InherentConstSelf { def_id: assoc_item }, ty::AssocTag::Fn => unreachable!(), }; @@ -1948,7 +1948,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.require_type_const_attribute(item_def_id, span)?; let alias_const = ty::AliasConst::new( tcx, - ty::AliasConstKind::new_from_def_id(tcx, item_def_id), + ty::AliasConstKind::new_from_def_id( + tcx, + item_def_id, + ty::AliasConstInherentArgsKind::WithSelf, + ), item_args, ); Ok(Const::new_alias(tcx, ty::IsRigid::No, alias_const)) @@ -2903,7 +2907,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Const::new_alias( tcx, ty::IsRigid::No, - ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, did), args), + ty::AliasConst::new( + tcx, + ty::AliasConstKind::new_from_def_id( + tcx, + did, + ty::AliasConstInherentArgsKind::WithSelf, + ), + args, + ), ) } Res::Def(kind @ DefKind::Ctor(ctor_of, CtorKind::Const), did) => { @@ -3141,14 +3153,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx(); - // FIXME(gca): Intentionally disallowing paths to inherent associated non-type constants - // until a refactoring for how generic args for IACs are represented has been landed. - let is_inherent_assoc_const = tcx.def_kind(def_id) - == DefKind::AssocConst { is_type_const: false } - && tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false }; - if tcx.is_type_const(def_id) - || tcx.features().generic_const_args() && !is_inherent_assoc_const - { + if tcx.is_type_const(def_id) || tcx.features().generic_const_args() { Ok(()) } else { let mut err = self.dcx().struct_span_err( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 651b4ca33be99..b59dc21981ce6 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -48,38 +48,6 @@ use crate::method::{self, MethodCallee}; use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Transform generic args for inherent associated type constants (IACs). - /// - /// IACs have a different generic parameter structure than regular associated constants: - /// - Regular assoc const: parent (impl) generic params + own generic params - /// - IAC (type_const): Self type + own generic params - pub(crate) fn transform_args_for_inherent_type_const( - &self, - def_id: DefId, - args: GenericArgsRef<'tcx>, - ) -> GenericArgsRef<'tcx> { - let tcx = self.tcx; - if !tcx.is_type_const(def_id) { - return args; - } - let Some(assoc_item) = tcx.opt_associated_item(def_id) else { - return args; - }; - if !matches!(assoc_item.container, ty::AssocContainer::InherentImpl) { - return args; - } - - let impl_def_id = assoc_item.container_id(tcx); - let generics = tcx.generics_of(def_id); - let impl_args = &args[..generics.parent_count]; - let self_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args).skip_norm_wip(); - // Build new args: [Self, own_args...] - let own_args = &args[generics.parent_count..]; - tcx.mk_args_from_iter( - std::iter::once(ty::GenericArg::from(self_ty)).chain(own_args.iter().copied()), - ) - } - /// Produces warning on the given node, if the current point in the /// function is unreachable, and there hasn't been another warning. pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) { @@ -1399,7 +1367,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let args_raw = implicit_args.unwrap_or_else(|| { + let args_for_user_type = implicit_args.unwrap_or_else(|| { lower_generic_args( self, def_id, @@ -1417,17 +1385,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }); - let args_for_user_type = if let Res::Def(DefKind::AssocConst { .. }, def_id) = res { - self.transform_args_for_inherent_type_const(def_id, args_raw) - } else { - args_raw - }; - // First, store the "user args" for later. self.write_user_type_annotation_from_args(hir_id, def_id, args_for_user_type, user_self_ty); // Normalize only after registering type annotations. - let args = self.normalize(span, Unnormalized::new_wip(args_raw)); + let args = self.normalize(span, Unnormalized::new_wip(args_for_user_type)); self.add_required_obligations_for_hir(span, def_id, args, hir_id); @@ -1465,12 +1427,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_instantiated); - let args = if let Res::Def(DefKind::AssocConst { .. }, def_id) = res { - self.transform_args_for_inherent_type_const(def_id, args) - } else { - args - }; - self.write_args(hir_id, args); (ty_instantiated, res) diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 57fd6a8658ae3..0f977710fbe09 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -392,7 +392,8 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti impl_def_id, impl_trait_ref.args, ); - tcx.check_args_compatible(trait_item_def_id, args) + let alias_kind = ty::AliasTermKind::ProjectionConst { def_id: trait_item_def_id }; + tcx.check_alias_term_args_compatible(alias_kind, args) .then(|| tcx.type_of(trait_item_def_id).instantiate(tcx, args).skip_norm_wip()) } else { Some(fcx.next_ty_var(span)) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 6f5d3bb75ebe4..e193f28f9738d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -998,7 +998,8 @@ impl<'tcx> InferCtxt<'tcx> { | ty::AliasTermKind::OpaqueTy { .. } | ty::AliasTermKind::FreeTy { .. } => self.next_ty_var(span).into(), ty::AliasTermKind::FreeConst { .. } - | ty::AliasTermKind::InherentConst { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::AnonConst { .. } | ty::AliasTermKind::ProjectionConst { .. } => self.next_const_var(span).into(), } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index afdabb38c3b20..35d04597451c0 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -182,7 +182,8 @@ impl<'tcx> InferCtxt<'tcx> { | ty::AliasTermKind::OpaqueTy { .. } => { return Err(TypeError::CyclicTy(source_term.expect_type())); } - ty::AliasTermKind::InherentConst { .. } + ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::FreeConst { .. } | ty::AliasTermKind::AnonConst { .. } => { return Err(TypeError::CyclicConst(source_term.expect_const())); diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 3cace48e3fb27..b030f0e07b2d6 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -13,7 +13,6 @@ use syn::spanned::Spanned; use syn::{Attribute, Field, LitStr, Meta, Path, Token, Type, TypeTuple, parenthesized}; use synstructure::{BindingInfo, VariantInfo}; -use super::error::invalid_attr; use crate::diagnostics::error::{ DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err, }; @@ -542,16 +541,6 @@ impl SuggestionKind { } } } - - fn from_suffix(s: &str) -> Option { - match s { - "" => Some(SuggestionKind::Normal), - "_short" => Some(SuggestionKind::Short), - "_hidden" => Some(SuggestionKind::Hidden), - "_verbose" => Some(SuggestionKind::Verbose), - _ => None, - } - } } /// Types of subdiagnostics that can be created using attributes @@ -569,7 +558,7 @@ pub(super) enum SubdiagnosticKind { HelpOnce, /// `#[warning(...)]` Warn, - /// `#[suggestion{,_short,_hidden,_verbose}]` + /// `#[suggestion(..)]` Suggestion { suggestion_kind: SuggestionKind, applicability: SpannedOption, @@ -580,7 +569,7 @@ pub(super) enum SubdiagnosticKind { /// `let __formatted_code = /* whatever */;` code_init: TokenStream, }, - /// `#[multipart_suggestion{,_short,_hidden,_verbose}]` + /// `#[multipart_suggestion(..)]` MultipartSuggestion { suggestion_kind: SuggestionKind, applicability: SpannedOption, @@ -618,44 +607,18 @@ impl SubdiagnosticVariant { "help" => SubdiagnosticKind::Help, "help_once" => SubdiagnosticKind::HelpOnce, "warning" => SubdiagnosticKind::Warn, + "suggestion" => SubdiagnosticKind::Suggestion { + suggestion_kind: SuggestionKind::Normal, + applicability: None, + code_field: new_code_ident(), + code_init: TokenStream::new(), + }, + "multipart_suggestion" => SubdiagnosticKind::MultipartSuggestion { + suggestion_kind: SuggestionKind::Normal, + applicability: None, + }, _ => { - // Recover old `#[(multipart_)suggestion_*]` syntaxes - // FIXME(#100717): remove - if let Some(suggestion_kind) = - name.strip_prefix("suggestion").and_then(SuggestionKind::from_suffix) - { - if suggestion_kind != SuggestionKind::Normal { - invalid_attr(attr) - .help(format!( - r#"Use `#[suggestion(..., style = "{suggestion_kind}")]` instead"# - )) - .emit(); - } - - SubdiagnosticKind::Suggestion { - suggestion_kind: SuggestionKind::Normal, - applicability: None, - code_field: new_code_ident(), - code_init: TokenStream::new(), - } - } else if let Some(suggestion_kind) = - name.strip_prefix("multipart_suggestion").and_then(SuggestionKind::from_suffix) - { - if suggestion_kind != SuggestionKind::Normal { - invalid_attr(attr) - .help(format!( - r#"Use `#[multipart_suggestion(..., style = "{suggestion_kind}")]` instead"# - )) - .emit(); - } - - SubdiagnosticKind::MultipartSuggestion { - suggestion_kind: SuggestionKind::Normal, - applicability: None, - } - } else { - throw_invalid_attr!(attr); - } + throw_invalid_attr!(attr); } }; diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index ec7495f95ac3a..f632862dc4627 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -191,10 +191,7 @@ decl_derive!( primary_span, label, subdiagnostic, - suggestion, - suggestion_short, - suggestion_hidden, - suggestion_verbose)] => + suggestion)] => #[doc = "See "] diagnostics::diagnostic_derive ); @@ -209,12 +206,7 @@ decl_derive!( warning, subdiagnostic, suggestion, - suggestion_short, - suggestion_hidden, - suggestion_verbose, multipart_suggestion, - multipart_suggestion_short, - multipart_suggestion_hidden, // field attributes primary_span, suggestion_part, diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 54e64b37245c3..3b85651ee5f76 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -474,7 +474,15 @@ impl<'tcx> UnevaluatedConst<'tcx> { #[inline] pub fn shrink(self, tcx: TyCtxt<'tcx>) -> ty::AliasConst<'tcx> { assert_eq!(self.promoted, None); - ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, self.def), self.args) + ty::AliasConst::new( + tcx, + ty::AliasConstKind::new_from_def_id( + tcx, + self.def, + ty::AliasConstInherentArgsKind::Impl, + ), + self.args, + ) } } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 9b98f4787371b..406a96ff7ca57 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -104,8 +104,11 @@ impl<'tcx> TyCtxt<'tcx> { } let def_id = match ct.kind { + ty::AliasConstKind::InherentSelf { .. } => { + bug!("got AliasConstKind::InherentSelf in const_eval_resolve_for_typeck") + } ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => def_id, }; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 2bb886ee167a3..86c5aaf4a46ed 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1504,7 +1504,8 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { ty::ConstKind::Alias(_, alias_const) => { let kind = match alias_const.kind { ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => self.tcx.def_path_str(def_id), }; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 7a3b4c7fbbeb8..924dc7552e59b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -13,7 +13,7 @@ use std::hash::{Hash, Hasher}; use std::marker::PointeeSized; use std::ops::Deref; use std::sync::{Arc, OnceLock}; -use std::{fmt, iter, mem}; +use std::{debug_assert_matches, fmt, iter, mem}; use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx}; use rustc_ast as ast; @@ -2117,27 +2117,44 @@ impl<'tcx> TyCtxt<'tcx> { if pred.kind() != binder { self.mk_predicate(binder) } else { pred } } + /// If you have a [`ty::Alias`], you should almost certainly be calling + /// [`Self::check_alias_term_args_compatible`] instead. This method assumes that inherent alias + /// consts always have `impl`-form args, and will return an invalid result if the `def_id` comes + /// from a [`ty::AliasConstKind::InherentSelf`] (see the doc on that for what "impl form args" + /// means). pub fn check_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) -> bool { - self.check_args_compatible_inner(def_id, args, false) + let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) + && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); + self.check_args_compatible_inner(def_id, args, is_inherent_assoc_ty) + } + + pub fn check_alias_term_args_compatible( + self, + kind: ty::AliasTermKind<'tcx>, + args: &'tcx [ty::GenericArg<'tcx>], + ) -> bool { + let (def_id, is_self_args) = match kind { + ty::AliasTermKind::ProjectionTy { def_id } + | ty::AliasTermKind::OpaqueTy { def_id } + | ty::AliasTermKind::FreeTy { def_id } + | ty::AliasTermKind::AnonConst { def_id } + | ty::AliasTermKind::ProjectionConst { def_id } + | ty::AliasTermKind::FreeConst { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => (def_id, false), + ty::AliasTermKind::InherentTy { def_id } + | ty::AliasTermKind::InherentConstSelf { def_id } => (def_id, true), + }; + self.check_args_compatible_inner(def_id, args, is_self_args) } fn check_args_compatible_inner( self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>], - nested: bool, + is_self_args: bool, ) -> bool { let generics = self.generics_of(def_id); - - // IATs and IACs (inherent associated types/consts with `type const`) themselves have a - // weird arg setup (self + own args), but nested items *in* IATs (namely: opaques, i.e. - // ATPITs) do not. - let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) - && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); - let is_inherent_assoc_type_const = - matches!(self.def_kind(def_id), DefKind::AssocConst { is_type_const: true }) - && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); - let own_args = if !nested && (is_inherent_assoc_ty || is_inherent_assoc_type_const) { + let own_args = if is_self_args { if generics.own_params.len() + 1 != args.len() { return false; } @@ -2154,8 +2171,11 @@ impl<'tcx> TyCtxt<'tcx> { let (parent_args, own_args) = args.split_at(generics.parent_count); + // In the type system, IATs and IACs (inherent associated types/consts) themselves have a + // weird arg setup (self + own args), but nested items *in* IATs (namely: opaques, i.e. + // ATPITs) do not. So, set `is_self_args` to false for the parent generic check. if let Some(parent) = generics.parent - && !self.check_args_compatible_inner(parent, parent_args, true) + && !self.check_args_compatible_inner(parent, parent_args, false) { return false; } @@ -2177,39 +2197,116 @@ impl<'tcx> TyCtxt<'tcx> { /// With `cfg(debug_assertions)`, assert that args are compatible with their generics, /// and print out the args if not. + /// + /// If you have a [`ty::Alias`], you should use + /// [`Self::debug_assert_alias_term_args_compatible`] instead. See note on + /// [`Self::check_args_compatible`]. pub fn debug_assert_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) { if cfg!(debug_assertions) && !self.check_args_compatible(def_id, args) { let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); - let is_inherent_assoc_type_const = - matches!(self.def_kind(def_id), DefKind::AssocConst { is_type_const: true }) - && matches!( - self.def_kind(self.parent(def_id)), - DefKind::Impl { of_trait: false } - ); - if is_inherent_assoc_ty || is_inherent_assoc_type_const { - bug!( - "args not compatible with generics for {}: args={:#?}, generics={:#?}", - self.def_path_str(def_id), - args, - // Make `[Self, GAT_ARGS...]` (this could be simplified) - self.mk_args_from_iter( - [self.types.self_param.into()].into_iter().chain( - self.generics_of(def_id) - .own_args(ty::GenericArgs::identity_for_item(self, def_id)) - .iter() - .copied() - ) - ) + self.emit_bug_args_compatible(def_id, args, is_inherent_assoc_ty); + } + } + + pub fn debug_assert_alias_term_args_compatible( + self, + kind: ty::AliasTermKind<'tcx>, + args: ty::GenericArgsRef<'tcx>, + ) { + if cfg!(debug_assertions) { + self.debug_assert_alias_term_kind_matches_def_kind(kind); + if !self.check_alias_term_args_compatible(kind, args) { + let (def_id, is_self_args) = match kind { + ty::AliasTermKind::ProjectionTy { def_id } + | ty::AliasTermKind::OpaqueTy { def_id } + | ty::AliasTermKind::FreeTy { def_id } + | ty::AliasTermKind::AnonConst { def_id } + | ty::AliasTermKind::ProjectionConst { def_id } + | ty::AliasTermKind::FreeConst { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => (def_id, false), + ty::AliasTermKind::InherentTy { def_id } + | ty::AliasTermKind::InherentConstSelf { def_id } => (def_id, true), + }; + self.emit_bug_args_compatible(def_id, args, is_self_args); + } + } + } + + fn debug_assert_alias_term_kind_matches_def_kind(self, kind: ty::AliasTermKind<'tcx>) { + match kind { + ty::AliasTermKind::ProjectionTy { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy); + debug_assert_matches!( + self.def_kind(self.parent(def_id)), + DefKind::Trait | DefKind::Impl { of_trait: true } + ); + } + ty::AliasTermKind::InherentTy { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy); + debug_assert_matches!( + self.def_kind(self.parent(def_id)), + DefKind::Impl { of_trait: false } + ); + } + ty::AliasTermKind::OpaqueTy { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::OpaqueTy); + } + ty::AliasTermKind::FreeTy { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::TyAlias); + } + ty::AliasTermKind::AnonConst { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::AnonConst); + } + ty::AliasTermKind::ProjectionConst { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocConst { .. }); + debug_assert_matches!( + self.def_kind(self.parent(def_id)), + DefKind::Trait | DefKind::Impl { of_trait: true } ); - } else { - bug!( - "args not compatible with generics for {}: args={:#?}, generics={:#?}", - self.def_path_str(def_id), - args, - ty::GenericArgs::identity_for_item(self, def_id) + } + ty::AliasTermKind::InherentConstSelf { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocConst { .. }); + debug_assert_matches!( + self.def_kind(self.parent(def_id)), + DefKind::Impl { of_trait: false } ); } + ty::AliasTermKind::FreeConst { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::Const { .. }); + } + } + } + + fn emit_bug_args_compatible( + self, + def_id: DefId, + args: &'tcx [ty::GenericArg<'tcx>], + is_self_args: bool, + ) -> ! { + if is_self_args { + bug!( + "args not compatible with generics for {}: args={:#?}, generics={:#?}", + self.def_path_str(def_id), + args, + // Make `[Self, GAT_ARGS...]` (this could be simplified) + self.mk_args_from_iter( + [self.types.self_param.into()].into_iter().chain( + self.generics_of(def_id) + .own_args(ty::GenericArgs::identity_for_item(self, def_id)) + .iter() + .copied() + ) + ) + ); + } else { + bug!( + "args not compatible with generics for {}: args={:#?}, generics={:#?}", + self.def_path_str(def_id), + args, + ty::GenericArgs::identity_for_item(self, def_id) + ); } } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 048e509ec88e0..576fdd8cb6053 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -204,11 +204,22 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.adt_def(adt_def_id) } - fn alias_const_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasConstKind<'tcx> { + fn alias_const_kind_from_def_id( + self, + def_id: Self::DefId, + inherent_args: ty::AliasConstInherentArgsKind, + ) -> ty::AliasConstKind<'tcx> { match self.def_kind(def_id) { DefKind::AssocConst { .. } => { if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - ty::AliasConstKind::Inherent { def_id } + match inherent_args { + ty::AliasConstInherentArgsKind::WithSelf => { + ty::AliasConstKind::InherentSelf { def_id } + } + ty::AliasConstInherentArgsKind::Impl => { + ty::AliasConstKind::InherentImpl { def_id } + } + } } else { ty::AliasConstKind::Projection { def_id } } @@ -221,7 +232,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } } - fn alias_term_kind_from_def_id(self, def_id: DefId) -> ty::AliasTermKind<'tcx> { + fn alias_term_kind_from_def_id( + self, + def_id: DefId, + inherent_args: ty::AliasConstInherentArgsKind, + ) -> ty::AliasTermKind<'tcx> { match self.def_kind(def_id) { DefKind::AssocTy => { if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { @@ -232,7 +247,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } DefKind::AssocConst { .. } => { if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - ty::AliasTermKind::InherentConst { def_id } + match inherent_args { + ty::AliasConstInherentArgsKind::WithSelf => { + ty::AliasTermKind::InherentConstSelf { def_id } + } + ty::AliasConstInherentArgsKind::Impl => { + ty::AliasTermKind::InherentConstImpl { def_id } + } + } } else { ty::AliasTermKind::ProjectionConst { def_id } } @@ -271,14 +293,26 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.mk_args_from_iter(args) } - fn check_args_compatible(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> bool { - self.check_args_compatible(def_id, args) + fn check_alias_term_args_compatible( + self, + kind: ty::AliasTermKind<'tcx>, + args: ty::GenericArgsRef<'tcx>, + ) -> bool { + self.check_alias_term_args_compatible(kind, args) } fn debug_assert_args_compatible(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) { self.debug_assert_args_compatible(def_id, args); } + fn debug_assert_alias_term_args_compatible( + self, + kind: ty::AliasTermKind<'tcx>, + args: ty::GenericArgsRef<'tcx>, + ) { + self.debug_assert_alias_term_args_compatible(kind, args); + } + /// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection` /// are compatible with the `DefId`. Since we're missing a `Self` type, stick on /// a dummy self type and forward to `debug_assert_args_compatible`. diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 33541dee52fe6..fb4e30b161d44 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -334,7 +334,8 @@ impl<'tcx> TyCtxt<'tcx> { | ty::AliasTermKind::AnonConst { def_id } | ty::AliasTermKind::ProjectionConst { def_id } | ty::AliasTermKind::FreeConst { def_id } - | ty::AliasTermKind::InherentConst { def_id } => self.def_path_str(def_id), + | ty::AliasTermKind::InherentConstSelf { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => self.def_path_str(def_id), } } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f055051580e81..f5960e65c4493 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1539,7 +1539,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ConstKind::Alias(_, ty::AliasConst { kind, args, .. }) => { match kind { ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } => { self.pretty_print_value_path(def_id, args)?; } @@ -3172,7 +3173,7 @@ define_print! { ty::AliasTerm<'tcx> { match self.kind { - ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConst { .. } => { + ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConstSelf { .. } => { p.pretty_print_inherent_projection(*self)?; } ty::AliasTermKind::ProjectionTy { def_id } => { @@ -3188,7 +3189,8 @@ define_print! { | ty::AliasTermKind::FreeConst { def_id } | ty::AliasTermKind::OpaqueTy { def_id } | ty::AliasTermKind::AnonConst { def_id } - | ty::AliasTermKind::ProjectionConst { def_id } => { + | ty::AliasTermKind::ProjectionConst { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => { p.print_def_path(def_id, self.args)?; } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index bef267b7eaf27..013064b5cec4b 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -478,22 +478,6 @@ impl<'tcx> Ty<'tcx> { is_rigid: ty::IsRigid, alias_ty: ty::AliasTy<'tcx>, ) -> Ty<'tcx> { - if cfg!(debug_assertions) { - match alias_ty.kind { - ty::AliasTyKind::Projection { def_id } => { - debug_assert_matches!(tcx.def_kind(def_id), DefKind::AssocTy) - } - ty::AliasTyKind::Inherent { def_id } => { - debug_assert_matches!(tcx.def_kind(def_id), DefKind::AssocTy) - } - ty::AliasTyKind::Opaque { def_id } => { - debug_assert_matches!(tcx.def_kind(def_id), DefKind::OpaqueTy) - } - ty::AliasTyKind::Free { def_id } => { - debug_assert_matches!(tcx.def_kind(def_id), DefKind::TyAlias) - } - } - } Ty::new(tcx, Alias(is_rigid, alias_ty)) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 09963dba563ec..622086b56c638 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -962,7 +962,8 @@ impl<'tcx> TyCtxt<'tcx> { } ty::AliasTermKind::OpaqueTy { def_id } => Some(self.variances_of(def_id)), ty::AliasTermKind::InherentTy { .. } - | ty::AliasTermKind::InherentConst { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } | ty::AliasTermKind::AnonConst { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 6e09c365dbf7c..5996073241e2c 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -74,7 +74,11 @@ pub(crate) fn as_constant_inner<'tcx>( if tcx.is_type_const(def_id) { let uneval = ty::AliasConst::new( tcx, - ty::AliasConstKind::new_from_def_id(tcx, def_id), + ty::AliasConstKind::new_from_def_id( + tcx, + def_id, + ty::AliasConstInherentArgsKind::Impl, + ), args, ); let ct = ty::Const::new_alias(tcx, ty::IsRigid::No, uneval); diff --git a/compiler/rustc_mir_build/src/builder/matches/buckets.rs b/compiler/rustc_mir_build/src/builder/matches/buckets.rs index 0d2e9bf87585d..36d3d78c21ec0 100644 --- a/compiler/rustc_mir_build/src/builder/matches/buckets.rs +++ b/compiler/rustc_mir_build/src/builder/matches/buckets.rs @@ -2,12 +2,12 @@ use std::cmp::Ordering; use rustc_data_structures::fx::FxIndexMap; use rustc_middle::mir::Place; -use rustc_middle::span_bug; +use rustc_middle::{bug, span_bug}; use tracing::debug; use crate::builder::Builder; use crate::builder::matches::{ - Candidate, PatConstKind, SliceLenOp, Test, TestBranch, TestKind, TestableCase, + Candidate, MatchPairKind, PatConstKind, SliceLenOp, Test, TestBranch, TestKind, TestableCase, }; /// Output of [`Builder::partition_candidates_into_buckets`]. @@ -131,17 +131,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // than one, but it'd be very unusual to have two sides that // both require tests; you'd expect one side to be simplified // away.) - let (match_pair_index, match_pair) = candidate - .match_pairs - .iter() - .enumerate() - .find(|&(_, mp)| mp.place == Some(test_place))?; + let (match_pair_index, match_pair_testable_case) = + candidate.match_pairs.iter().enumerate().find_map(|(i, mp)| { + if let MatchPairKind::Testable { place, ref testable_case, .. } = mp.kind + && place == test_place + { + Some((i, testable_case)) + } else { + None + } + })?; // If true, the match pair is completely entailed by its corresponding test // branch, so it can be removed. If false, the match pair is _compatible_ // with its test branch, but still needs a more specific test. let fully_matched; - let ret = match (&test.kind, &match_pair.testable_case) { + let ret = match (&test.kind, match_pair_testable_case) { // If we are performing a variant switch, then this // informs variant patterns, but nothing else. ( @@ -174,7 +179,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; let is_conflicting_candidate = |candidate: &&mut Candidate<'tcx>| { candidate.match_pairs.iter().any(|mp| { - mp.place == Some(test_place) && is_covering_range(&mp.testable_case) + matches!(mp.kind, MatchPairKind::Testable { place, ref testable_case, .. } + if place == test_place && is_covering_range(testable_case) + ) }) }; if prior_candidates @@ -364,7 +371,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if fully_matched { // Replace the match pair by its sub-pairs. let match_pair = candidate.match_pairs.remove(match_pair_index); - candidate.match_pairs.extend(match_pair.subpairs); + let MatchPairKind::Testable { subpairs, .. } = match_pair.kind else { + bug!("match pair must have been refutable"); + }; + candidate.match_pairs.extend(subpairs); // Move or-patterns to the end. candidate.sort_match_pairs(); } diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index b4ce8149f5e4d..7ad21b3272783 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -10,7 +10,7 @@ use rustc_span::Span; use crate::builder::Builder; use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder}; use crate::builder::matches::{ - FlatPat, MatchPairTree, PatConstKind, PatternExtraData, SliceLenOp, TestableCase, + FlatPat, MatchPairKind, MatchPairTree, PatConstKind, PatternExtraData, SliceLenOp, TestableCase, }; /// For an array or slice pattern's subpatterns (prefix/slice/suffix), returns a list @@ -107,113 +107,73 @@ fn squash_inter_pat<'tcx>( extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here ) { // Destructure exhaustively to make sure we don't miss any fields. - let InterPat { - place, - testable_case, - subpats, - or_subpats, - ascriptions, - binding, - pattern_span, - is_never: _, // Not needed by `MatchPairTree` forests. - } = inter_pat; + // The `is_never` field is not needed by `MatchPairTree` forests. + let InterPat { kind, ascriptions, pattern_span, is_never: _ } = inter_pat; // Type ascriptions can appear regardless of whether the node is an or-pattern. extra_data.ascriptions.extend(ascriptions); - // Or and non-or patterns have very different handling. - if let Some(or_subpats) = or_subpats { - // We're dealing with an or-pattern node. - assert!(testable_case.is_none()); - assert!(subpats.is_empty()); - assert!(binding.is_none()); - - let or_subpats = or_subpats - .into_iter() - .map(|subpat| FlatPat::from_inter_pat(subpat)) - .collect::>(); - - if !or_subpats[0].extra_data.bindings.is_empty() { - // Hold a place for any bindings established in (possibly-nested) or-patterns. - // By only holding a place when bindings are present, we skip over any - // or-patterns that will be simplified by `merge_trivial_subcandidates`. In - // other words, we can assume this expands into subcandidates. - // FIXME(@dianne): this needs updating/removing if we always merge or-patterns - extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); - } + // Or patterns, refutable patterns, and irrefutable patterns all have different handling. + match kind { + InterPatKind::Or { or_subpats } => { + let or_subpats = or_subpats + .into_iter() + .map(|subpat| FlatPat::from_inter_pat(subpat)) + .collect::>(); + + if !or_subpats[0].extra_data.bindings.is_empty() { + // Hold a place for any bindings established in (possibly-nested) or-patterns. + // By only holding a place when bindings are present, we skip over any + // or-patterns that will be simplified by `merge_trivial_subcandidates`. In + // other words, we can assume this expands into subcandidates. + // FIXME(@dianne): this needs updating/removing if we always merge or-patterns + extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); + } - match_pairs.push(MatchPairTree { - // Or-patterns never need a place during MIR building. - place: None, - testable_case: TestableCase::Or { pats: or_subpats }, - subpairs: vec![], - pattern_span, - }); - } else { - // We're dealing with a node that isn't an or-pattern. - - // Recursively squash any subpatterns into refutable `MatchPairTree` forests. - // This must happen _before_ pushing the binding, as described by the binding step. - let mut subpairs = vec![]; - for subpat in subpats { - squash_inter_pat(subpat, &mut subpairs, extra_data); + match_pairs + .push(MatchPairTree { kind: MatchPairKind::Or { or_subpats }, pattern_span }); } - if let Some(testable_case) = testable_case { + InterPatKind::Refutable { place, testable_case, subpats } => { + // Recursively squash any subpatterns into refutable `MatchPairTree` forests, + // which will become the children of a new node. + let mut subpairs = vec![]; + for subpat in subpats { + squash_inter_pat(subpat, &mut subpairs, extra_data); + } + // This pattern is refutable, so push a new match-pair node. - // - // If this match is inside a closure, it's essential that the place - // we're testing was actually captured! Be sure to keep `ExprUseVisitor` - // in sync with the refutability checks in this module. - assert!(place.is_some()); - assert!(!matches!(testable_case, TestableCase::Or { .. })); - match_pairs.push(MatchPairTree { place, testable_case, subpairs, pattern_span }); - } else { - // This pattern is irrefutable, so it doesn't need its own match-pair node. - // Just push its refutable subpatterns instead, if any. - match_pairs.extend(subpairs); + match_pairs.push(MatchPairTree { + kind: MatchPairKind::Testable { place, testable_case, subpairs }, + pattern_span, + }); } - // If present, the binding must be pushed _after_ traversing subpatterns. - // This is so that when lowering something like `x @ NonCopy { copy_field }`, - // the binding to `copy_field` will occur before the binding for `x`. - // See for more background. - if let Some(binding) = binding { - extra_data.bindings.push(super::SubpatternBindings::One(binding)); + InterPatKind::Irrefutable { subpats, binding } => { + // Recursively squash any subpatterns into refutable `MatchPairTree` forests. + // This must happen _before_ pushing the binding, as described by the binding step. + for subpat in subpats { + // For irrefutable nodes, squash directly into the caller's match pairs. + squash_inter_pat(subpat, match_pairs, extra_data); + } + + // If present, the binding must be pushed _after_ traversing subpatterns. + // This is so that when lowering something like `x @ NonCopy { copy_field }`, + // the binding to `copy_field` will occur before the binding for `x`. + // See for more background. + if let Some(binding) = binding { + extra_data.bindings.push(super::SubpatternBindings::One(binding)); + } } } } /// "Intermediate pattern", a partly-lowered THIR [`Pat`] that has not yet been /// squashed into a forest of refutable [`MatchPairTree`] nodes. -/// -/// FIXME(Zalathar): This could potentially be split into different enum variants -/// for or-patterns and non-or patterns, but for now the flat structure makes -/// construction a bit easier, at the cost of more complicated invariants. struct InterPat<'tcx> { - /// Place that this pattern node will test. - /// - /// If `None`, we're in a closure that didn't capture the relevant place, - /// because it won't actually be tested. - place: Option>, - /// Testable condition to compare the place to (e.g. "is 3" or "is Some"). - /// - /// If `None`, this pattern node is irrefutable or an or-pattern, - /// though it might have refutable descendants. - testable_case: Option>, - - /// Immediate subpatterns of a node that is *not* an or-pattern. - subpats: Vec>, - /// Immediate subpatterns of an or-pattern node. - /// - /// Invariant: If this is Some, then fields `subpats`, `testable_case`, - /// and `binding` must all be empty. - or_subpats: Option]>>, + kind: InterPatKind<'tcx>, ascriptions: Vec>, - /// Binding to establish for a [`PatKind::Binding`] node. - binding: Option>, - /// Span field of the THIR pattern this node was created from. pattern_span: Span, /// True if this pattern can never match, because all of its alternatives @@ -221,6 +181,31 @@ struct InterPat<'tcx> { is_never: bool, } +enum InterPatKind<'tcx> { + Or { + /// The alternatives of an or-pattern, e.g. `P` and `Q` in `P | Q`. + or_subpats: Box<[InterPat<'tcx>]>, + }, + + /// Pattern node that performs some kind of test on a place. + Refutable { + /// Place that this pattern node will test. + place: Place<'tcx>, + /// Testable condition to compare the place to (e.g. "is 3" or "is Some"). + testable_case: TestableCase<'tcx>, + /// Immediate subpatterns. + subpats: Vec>, + }, + + /// Pattern node that doesn't test anything, though it might have refutable descendants. + Irrefutable { + /// Immediate subpatterns. + subpats: Vec>, + /// Binding to establish for a [`PatKind::Binding`] node. + binding: Option>, + }, +} + impl<'tcx> InterPat<'tcx> { fn lower_thir_pat( cx: &mut Builder<'_, 'tcx>, @@ -250,44 +235,49 @@ impl<'tcx> InterPat<'tcx> { } } - // Variables that will become `InterPat` fields: let place = place_builder.try_to_place(cx); - let mut subpats = vec![]; - let mut or_subpats = None; - let mut ascriptions = vec![]; - let mut binding = None; // Apply any type ascriptions to the value at `match_pair.place`. + let mut ascriptions = vec![]; if let Some(place) = place && let Some(extra) = &pattern.extra { - for &Ascription { ref annotation, variance } in &extra.ascriptions { - ascriptions.push(super::Ascription { + ascriptions.extend(extra.ascriptions.iter().map( + |&Ascription { ref annotation, variance }| super::Ascription { source: place, annotation: annotation.clone(), variance, - }); - } + }, + )); } - let testable_case = match pattern.kind { - PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, + // For refutable nodes a place must be available, either because it is not a + // closure upvar or because it was captured. + let unwrap_place = || place.expect("refutable patterns must have captured a place"); + + let kind: InterPatKind<'_> = match pattern.kind { + PatKind::Missing | PatKind::Wild | PatKind::Error(_) => { + InterPatKind::Irrefutable { subpats: vec![], binding: None } + } PatKind::Or { ref pats } => { - or_subpats = Some( - pats.iter() - .map(|subpat| InterPat::lower_thir_pat(cx, place_builder.clone(), subpat)) - .collect::>(), - ); - None + let or_subpats = pats + .iter() + .map(|subpat| InterPat::lower_thir_pat(cx, place_builder.clone(), subpat)) + .collect::>(); + InterPatKind::Or { or_subpats } } PatKind::Range(ref range) => { assert_eq!(pattern.ty, range.ty); if range.is_full_range(cx.tcx) == Some(true) { - None + InterPatKind::Irrefutable { subpats: vec![], binding: None } } else { - Some(TestableCase::Range(Arc::clone(range))) + InterPatKind::Refutable { + place: unwrap_place(), + testable_case: TestableCase::Range(Arc::clone(range)), + subpats: vec![], + } } } @@ -311,27 +301,30 @@ impl<'tcx> InterPat<'tcx> { // which could be split out into their own kinds. PatConstKind::Other }; - Some(TestableCase::Constant { value, kind: const_kind }) + + InterPatKind::Refutable { + place: unwrap_place(), + testable_case: TestableCase::Constant { value, kind: const_kind }, + subpats: vec![], + } } PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => { // First, recurse into the subpattern, if any. - if let Some(subpattern) = subpattern.as_ref() { - // this is the `x @ P` case; have to keep matching against `P` now - subpats.push(InterPat::lower_thir_pat(cx, place_builder, subpattern)); - } + // This is the `x @ P` case; have to keep matching against `P` now. + let subpat: Option> = subpattern + .as_deref() + .map(|subpattern| InterPat::lower_thir_pat(cx, place_builder, subpattern)); // Then push this binding, after any bindings in the subpattern. - if let Some(place) = place { - binding = Some(super::Binding { - span: pattern.span, - source: place, - var_id: var, - binding_mode: mode, - is_shorthand, - }); - } - None + let binding = place.map(|place| super::Binding { + span: pattern.span, + source: place, + var_id: var, + binding_mode: mode, + is_shorthand, + }); + InterPatKind::Irrefutable { subpats: Vec::from_iter(subpat), binding } } PatKind::Array { ref prefix, ref slice, ref suffix } => { @@ -343,6 +336,8 @@ impl<'tcx> InterPat<'tcx> { ty::Array(_, len) => len.try_to_target_usize(cx.tcx), _ => None, }; + + let mut subpats = vec![]; if let Some(array_len) = array_len { for (subplace, subpat) in prefix_slice_suffix(&place_builder, Some(array_len), prefix, slice, suffix) @@ -361,9 +356,10 @@ impl<'tcx> InterPat<'tcx> { ); } - None + InterPatKind::Irrefutable { subpats, binding: None } } PatKind::Slice { ref prefix, ref slice, ref suffix } => { + let mut subpats = vec![]; for (subplace, subpat) in prefix_slice_suffix(&place_builder, None, prefix, slice, suffix) { @@ -373,24 +369,26 @@ impl<'tcx> InterPat<'tcx> { if prefix.is_empty() && slice.is_some() && suffix.is_empty() { // A slice pattern shaped like `[..]` is irrefutable. // It can match a slice of any length, so no length test is needed. - None + InterPatKind::Irrefutable { subpats, binding: None } } else { // Any other shape of slice pattern requires a length test. // Slice patterns with a `..` subpattern require a minimum // length; those without `..` require an exact length. - Some(TestableCase::Slice { + let testable_case = TestableCase::Slice { len: u64::try_from(prefix.len() + suffix.len()).unwrap(), op: if slice.is_some() { SliceLenOp::GreaterOrEqual } else { SliceLenOp::Equal }, - }) + }; + InterPatKind::Refutable { place: unwrap_place(), testable_case, subpats } } } PatKind::Variant { adt_def, variant_index, args: _, ref subpatterns } => { let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` + let mut subpats = vec![]; for &FieldPat { field, pattern: ref subpat } in subpatterns { let subplace = downcast_place.clone_project(PlaceElem::Field(field, subpat.ty)); subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); @@ -401,18 +399,20 @@ impl<'tcx> InterPat<'tcx> { let refutable = adt_def.variants().len() > 1 || adt_def.is_variant_list_non_exhaustive(); if refutable { - Some(TestableCase::Variant { adt_def, variant_index }) + let testable_case = TestableCase::Variant { adt_def, variant_index }; + InterPatKind::Refutable { place: unwrap_place(), testable_case, subpats } } else { - None + InterPatKind::Irrefutable { subpats, binding: None } } } PatKind::Leaf { ref subpatterns } => { + let mut subpats = vec![]; for &FieldPat { field, pattern: ref subpat } in subpatterns { let subplace = place_builder.clone_project(PlaceElem::Field(field, subpat.ty)); subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); } - None + InterPatKind::Irrefutable { subpats, binding: None } } PatKind::Deref { pin: Pinnedness::Pinned, ref subpattern } => { @@ -420,20 +420,20 @@ impl<'tcx> InterPat<'tcx> { Some(p_ty) if p_ty.is_ref() => p_ty, _ => span_bug!(pattern.span, "bad type for pinned deref: {:?}", pattern.ty), }; - subpats.push(InterPat::lower_thir_pat( + let subpat = InterPat::lower_thir_pat( cx, // Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`. place_builder.field(FieldIdx::ZERO, pinned_ref_ty).deref(), subpattern, - )); + ); - None + InterPatKind::Irrefutable { subpats: vec![subpat], binding: None } } PatKind::Deref { pin: Pinnedness::Not, ref subpattern } | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => { - subpats.push(InterPat::lower_thir_pat(cx, place_builder.deref(), subpattern)); - None + let subpat = InterPat::lower_thir_pat(cx, place_builder.deref(), subpattern); + InterPatKind::Irrefutable { subpats: vec![subpat], binding: None } } PatKind::DerefPattern { @@ -446,41 +446,39 @@ impl<'tcx> InterPat<'tcx> { Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability), pattern.span, ); - subpats.push(InterPat::lower_thir_pat( - cx, - PlaceBuilder::from(temp).deref(), - subpattern, - )); - Some(TestableCase::Deref { temp, mutability }) + let subpat = + InterPat::lower_thir_pat(cx, PlaceBuilder::from(temp).deref(), subpattern); + InterPatKind::Refutable { + place: unwrap_place(), + testable_case: TestableCase::Deref { temp, mutability }, + subpats: vec![subpat], + } } PatKind::Guard { .. } => { // FIXME(guard_patterns) - None + InterPatKind::Irrefutable { subpats: vec![], binding: None } } - PatKind::Never => Some(TestableCase::Never), + PatKind::Never => InterPatKind::Refutable { + place: unwrap_place(), + testable_case: TestableCase::Never, + subpats: vec![], + }, }; // A pattern node is guaranteed to never match if one of these is true: // - The node itself is a never pattern (`!`). // - It is not an or-pattern, and one of its subpatterns will never match. // - It is an or-pattern, and _all_ of its or-subpatterns will never match. - let is_never = matches!(pattern.kind, PatKind::Never) - || subpats.iter().any(|subpat| subpat.is_never) - || or_subpats - .as_ref() - .is_some_and(|or_subpats| or_subpats.iter().all(|subpat| subpat.is_never)); - - InterPat { - place, - testable_case, - subpats, - or_subpats, - ascriptions, - binding, - pattern_span: pattern.span, - is_never, - } + let is_never = match &kind { + InterPatKind::Refutable { testable_case: TestableCase::Never, .. } => true, + InterPatKind::Refutable { subpats, .. } | InterPatKind::Irrefutable { subpats, .. } => { + subpats.iter().any(|subpat| subpat.is_never) + } + InterPatKind::Or { or_subpats } => or_subpats.iter().all(|subpat| subpat.is_never), + }; + + InterPat { kind, ascriptions, pattern_span: pattern.span, is_never } } } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index ddeb9e084b21d..71ba6b73bec8b 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1030,7 +1030,7 @@ struct Candidate<'tcx> { /// (see [`Builder::test_remaining_match_pairs_after_or`]). /// /// Invariants: - /// - All or-patterns ([`TestableCase::Or`]) have been sorted to the end. + /// - All or-patterns ([`MatchPairKind::Or`]) have been sorted to the end. match_pairs: Vec>, /// ...and if this is non-empty, one of these subcandidates also has to match... @@ -1116,14 +1116,14 @@ impl<'tcx> Candidate<'tcx> { /// Restores the invariant that or-patterns must be sorted to the end. fn sort_match_pairs(&mut self) { - self.match_pairs.sort_by_key(|pair| matches!(pair.testable_case, TestableCase::Or { .. })); + self.match_pairs.sort_by_key(|pair| matches!(pair.kind, MatchPairKind::Or { .. })); } /// Returns whether the first match pair of this candidate is an or-pattern. fn starts_with_or_pattern(&self) -> bool { matches!( - &*self.match_pairs, - [MatchPairTree { testable_case: TestableCase::Or { .. }, .. }, ..] + self.match_pairs.first(), + Some(MatchPairTree { kind: MatchPairKind::Or { .. }, .. }) ) } @@ -1223,7 +1223,6 @@ enum TestableCase<'tcx> { Slice { len: u64, op: SliceLenOp }, Deref { temp: Place<'tcx>, mutability: Mutability }, Never, - Or { pats: Box<[FlatPat<'tcx>]> }, } impl<'tcx> TestableCase<'tcx> { @@ -1261,32 +1260,32 @@ enum PatConstKind { /// Each node also has a list of subpairs (possibly empty) that must also match, /// and some additional information from the THIR pattern it represents. #[derive(Debug, Clone)] -pub(crate) struct MatchPairTree<'tcx> { - /// This place... - /// - /// --- - /// This can be `None` if it referred to a non-captured place in a closure. - /// - /// Invariant: Can only be `None` when `testable_case` is `Or`. - /// Therefore this must be `Some(_)` after or-pattern expansion. - place: Option>, - - /// ... must pass this test... - testable_case: TestableCase<'tcx>, - - /// ... and these subpairs must match. - /// - /// --- - /// Subpairs typically represent tests that can only be performed after their - /// parent has succeeded. For example, the pattern `Some(3)` might have an - /// outer match pair that tests for the variant `Some`, and then a subpair - /// that tests its field for the value `3`. - subpairs: Vec, +struct MatchPairTree<'tcx> { + kind: MatchPairKind<'tcx>, /// Span field of the THIR pattern this node was created from. pattern_span: Span, } +#[derive(Debug, Clone)] +enum MatchPairKind<'tcx> { + Or { + or_subpats: Box<[FlatPat<'tcx>]>, + }, + Testable { + /// Place that will be tested. + place: Place<'tcx>, + /// Test to perform against the place, and the desired outcome. + testable_case: TestableCase<'tcx>, + + /// Further tests that can only be performed after this test has succeeded. + /// For example, in the pattern `Some(3)` this node might represent a test + /// for the variant `Some`, while a subpair would test its field for the + /// value `3`. + subpairs: Vec>, + }, +} + /// A runtime test to perform to determine which candidates match a scrutinee place. /// /// The kind of test to perform is indicated by [`TestKind`]. @@ -1948,10 +1947,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate: &mut Candidate<'tcx>, match_pair: MatchPairTree<'tcx>, ) { - let TestableCase::Or { pats } = match_pair.testable_case else { bug!() }; - debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats); + let MatchPairKind::Or { or_subpats } = match_pair.kind else { bug!() }; + debug!("expanding or-pattern: candidate={:#?}\nor_subpats={:#?}", candidate, or_subpats); candidate.or_span = Some(match_pair.pattern_span); - candidate.subcandidates = pats + candidate.subcandidates = or_subpats .into_iter() .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) .collect(); @@ -2116,7 +2115,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug_assert!( remaining_match_pairs .iter() - .all(|match_pair| matches!(match_pair.testable_case, TestableCase::Or { .. })) + .all(|match_pair| matches!(match_pair.kind, MatchPairKind::Or { .. })) ); // Visit each leaf candidate within this subtree, add a copy of the remaining @@ -2167,8 +2166,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Extract the match-pair from the highest priority candidate let match_pair = &candidates[0].match_pairs[0]; let test = self.pick_test_for_match_pair(match_pair); - // Unwrap is ok after simplification. - let match_place = match_pair.place.unwrap(); + + let MatchPairKind::Testable { place: match_place, .. } = match_pair.kind else { + bug!("match pair must be testable") + }; debug!(?test, ?match_pair); (match_place, test) diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 6c499315143c3..ca9519381c8e0 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -20,7 +20,8 @@ use tracing::{debug, instrument}; use crate::builder::Builder; use crate::builder::matches::{ - MatchPairTree, PatConstKind, SliceLenOp, Test, TestBranch, TestKind, TestableCase, + MatchPairKind, MatchPairTree, PatConstKind, SliceLenOp, Test, TestBranch, TestKind, + TestableCase, }; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -31,7 +32,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, match_pair: &MatchPairTree<'tcx>, ) -> Test<'tcx> { - let kind = match match_pair.testable_case { + // Or-patterns are not tested directly; instead they are expanded into subcandidates, + // which are then distinguished by testing whatever non-or patterns they contain. + let MatchPairKind::Testable { ref testable_case, .. } = match_pair.kind else { + bug!("or-patterns should have already been handled") + }; + let kind = match *testable_case { TestableCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def }, TestableCase::Constant { value: _, kind: PatConstKind::Bool } => TestKind::If, @@ -52,10 +58,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestableCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability }, TestableCase::Never => TestKind::Never, - - // Or-patterns are not tested directly; instead they are expanded into subcandidates, - // which are then distinguished by testing whatever non-or patterns they contain. - TestableCase::Or { .. } => bug!("or-patterns should have already been handled"), }; Test { span: match_pair.pattern_span, kind } diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index 3246dab73dcbf..fa94a41bad339 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -6,7 +6,9 @@ use tracing::debug; use crate::builder::Builder; use crate::builder::expr::as_place::PlaceBase; -use crate::builder::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestableCase}; +use crate::builder::matches::{ + Binding, Candidate, FlatPat, MatchPairKind, MatchPairTree, TestableCase, +}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Creates a false edge to `imaginary_target` and a real edge to @@ -159,35 +161,36 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'tcx>) { - if let TestableCase::Or { pats, .. } = &match_pair.testable_case { - for flat_pat in pats.iter() { - self.visit_flat_pat(flat_pat) - } - } else if matches!(match_pair.testable_case, TestableCase::Deref { .. }) { - // The subpairs of a deref pattern are all places relative to the deref temporary, so we - // don't fake borrow them. Problem is, if we only shallowly fake-borrowed - // `match_pair.place`, this would allow: - // ``` - // let mut b = Box::new(false); - // match b { - // deref!(true) => {} // not reached because `*b == false` - // _ if { *b = true; false } => {} // not reached because the guard is `false` - // deref!(false) => {} // not reached because the guard changed it - // // UB because we reached the unreachable. - // } - // ``` - // Hence we fake borrow using a deep borrow. - if let Some(place) = match_pair.place { - self.fake_borrow(place, FakeBorrowKind::Deep); - } - } else { - // Insert a Shallow borrow of any place that is switched on. - if let Some(place) = match_pair.place { - self.fake_borrow(place, FakeBorrowKind::Shallow); + match match_pair.kind { + MatchPairKind::Or { ref or_subpats } => { + for flat_pat in or_subpats { + self.visit_flat_pat(flat_pat); + } } + MatchPairKind::Testable { place, ref testable_case, ref subpairs } => { + if matches!(testable_case, TestableCase::Deref { .. }) { + // The subpairs of a deref pattern are all places relative to the deref temporary, so we + // don't fake borrow them. Problem is, if we only shallowly fake-borrowed + // `match_pair.place`, this would allow: + // ``` + // let mut b = Box::new(false); + // match b { + // deref!(true) => {} // not reached because `*b == false` + // _ if { *b = true; false } => {} // not reached because the guard is `false` + // deref!(false) => {} // not reached because the guard changed it + // // UB because we reached the unreachable. + // } + // ``` + // Hence we fake borrow using a deep borrow. + self.fake_borrow(place, FakeBorrowKind::Deep); + } else { + // Insert a Shallow borrow of any place that is switched on. + self.fake_borrow(place, FakeBorrowKind::Shallow); - for subpair in &match_pair.subpairs { - self.visit_match_pair(subpair); + for subpair in subpairs { + self.visit_match_pair(subpair); + } + } } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 0230840ef2fb8..86387f5caf325 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -80,14 +80,16 @@ impl<'tcx> ConstToPat<'tcx> { fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box> { if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() { if let ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } = alias_const.kind + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } = alias_const.kind && let Some(def_id) = def_id.as_local() { // Include the container item in the output. err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), ""); } if let ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } = alias_const.kind { err.span_label(self.tcx.def_span(def_id), msg!("constant defined here")); @@ -166,7 +168,8 @@ impl<'tcx> ConstToPat<'tcx> { // on its use as well. if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() && let ty::AliasConstKind::Projection { .. } - | ty::AliasConstKind::Inherent { .. } + | ty::AliasConstKind::InherentSelf { .. } + | ty::AliasConstKind::InherentImpl { .. } | ty::AliasConstKind::Free { .. } = alias_const.kind { err.downgrade_to_delayed_bug(); diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index b69519f3c714f..d64f98542b3a3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -658,7 +658,11 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { ty::IsRigid::No, ty::AliasConst::new( self.tcx, - ty::AliasConstKind::new_from_def_id(self.tcx, def_id), + ty::AliasConstKind::new_from_def_id( + self.tcx, + def_id, + ty::AliasConstInherentArgsKind::Impl, + ), args, ), ); diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index a0abc918107df..034ad3463ba13 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1074,7 +1074,8 @@ where | ty::AliasTermKind::OpaqueTy { .. } | ty::AliasTermKind::FreeTy { .. } => self.next_ty_infer().into(), ty::AliasTermKind::FreeConst { .. } - | ty::AliasTermKind::InherentConst { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::AnonConst { .. } | ty::AliasTermKind::ProjectionConst { .. } => self.next_const_infer().into(), } @@ -1440,12 +1441,15 @@ where if self.resolve_vars_if_possible(alias_const).has_non_region_infer() { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } else { + // Evaluation failed because the const was too generic or was an invalid type + // for const generics. The result of normalization is the alias itself, + // unchanged, but marked as rigid. + // // We do not instantiate to the `alias_const` passed in, but rather - // `goal.predicate.alias`. The `alias_const` passed in might correspond to the `impl` - // form of a constant (with generic arguments corresponding to the impl block), - // however, we want to structurally instantiate to the original, non-rebased, - // trait `Self` form of the constant (with generic arguments being the trait - // `Self` type). + // `projection_term`, which is the unprocessed, original alias contained within + // the goal. The `alias_const` passed in might be a Projection whose DefId is an + // impl of the trait, however, we want to structurally instantiate to the + // original DefId on the trait itself. self.eq( param_env, projection_term.to_term(self.cx(), ty::IsRigid::Yes), diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index f5b1df1be3eff..75f15623a9ba7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -417,7 +417,17 @@ where target_container_def_id, )?; - if !cx.check_args_compatible(target_item_def_id.into(), target_args) { + let target_item_def_id: I::DefId = target_item_def_id.into(); + + let target_item_kind = if goal.predicate.alias.kind.is_type() { + ty::AliasTermKind::ProjectionTy { def_id: target_item_def_id.try_into().unwrap() } + } else { + ty::AliasTermKind::ProjectionConst { + def_id: target_item_def_id.try_into().unwrap(), + } + }; + + if !cx.check_alias_term_args_compatible(target_item_kind, target_args) { return error_response( ecx, cx.delay_bug("associated item has mismatched arguments"), @@ -427,15 +437,14 @@ where // Finally we construct the actual value of the associated type. let term = match goal.predicate.alias.kind { ty::AliasTermKind::ProjectionTy { .. } => { - let t = cx.type_of(target_item_def_id.into()).instantiate(cx, target_args); + let t = cx.type_of(target_item_def_id).instantiate(cx, target_args); let t = ecx.normalize(GoalSource::Misc, goal.param_env, t)?; t.into() } ty::AliasTermKind::ProjectionConst { .. } - if cx.is_type_const(target_item_def_id.into()) => + if cx.is_type_const(target_item_def_id) => { - let c = - cx.const_of_item(target_item_def_id.into()).instantiate(cx, target_args); + let c = cx.const_of_item(target_item_def_id).instantiate(cx, target_args); let c = ecx.normalize(GoalSource::Misc, goal.param_env, c)?; c.into() } @@ -443,7 +452,7 @@ where let alias_const = ty::AliasConst::new( cx, ty::AliasConstKind::Projection { - def_id: target_item_def_id.into().try_into().unwrap(), + def_id: target_item_def_id.try_into().unwrap(), }, target_args, ); @@ -827,13 +836,7 @@ where CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionClause { - projection_term: ty::AliasTerm::new( - ecx.cx(), - cx.alias_term_kind_from_def_id( - goal.predicate.alias.expect_projection_def_id().into(), - ), - [self_ty], - ), + projection_term: ty::AliasTerm::new(ecx.cx(), goal.predicate.alias.kind, [self_ty]), term, } .upcast(cx), @@ -865,13 +868,7 @@ where CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionClause { - projection_term: ty::AliasTerm::new( - ecx.cx(), - cx.alias_term_kind_from_def_id( - goal.predicate.alias.expect_projection_def_id().into(), - ), - [self_ty], - ), + projection_term: ty::AliasTerm::new(ecx.cx(), goal.predicate.alias.kind, [self_ty]), term, } .upcast(cx), diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs index a7480cded0514..51c2475a33461 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs @@ -5,7 +5,7 @@ //! 2. equate the self type, and //! 3. instantiate and register where clauses. -use rustc_type_ir::solve::QueryResultOrRerunNonErased; +use rustc_type_ir::solve::{NoSolutionOrRerunNonErased, QueryResultOrRerunNonErased}; use rustc_type_ir::{self as ty, Interner, Unnormalized}; use crate::delegate::SolverDelegate; @@ -21,20 +21,9 @@ where goal: Goal>, ) -> QueryResultOrRerunNonErased { let cx = self.cx(); - let inherent = goal.predicate.projection_term; - let def_id = inherent.expect_inherent_def_id(); - let impl_def_id = cx.inherent_alias_term_parent(def_id); - let impl_args = self.fresh_args_for_item(impl_def_id.into()); - - // Equate impl header and add impl where clauses - self.eq( - goal.param_env, - inherent.self_ty(), - cx.type_of(impl_def_id.into()).instantiate(cx, impl_args).skip_norm_wip(), - )?; - - // Equate IAT with the RHS of the project goal - let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, cx); + let def_id = goal.predicate.projection_term.expect_inherent_def_id(); + let (inherent_kind, inherent_args) = + self.convert_inherent_self_to_impl(goal.param_env, goal.predicate.projection_term)?; // Check both where clauses on the impl and IAT // @@ -53,25 +42,28 @@ where .map(|clause| goal.with(cx, clause)), )?; - let normalized: I::Term = match inherent.kind { + let normalized: I::Term = match inherent_kind { ty::AliasTermKind::InherentTy { def_id } => { let inherent = cx.type_of(def_id.into()).instantiate(cx, inherent_args); let inherent = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; inherent.into() } - ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id.into()) => { + ty::AliasTermKind::InherentConstImpl { def_id } if cx.is_type_const(def_id.into()) => { let inherent = cx.const_of_item(def_id.into()).instantiate(cx, inherent_args); let inherent = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; inherent.into() } - ty::AliasTermKind::InherentConst { .. } => { - // FIXME(gca): This is dead code at the moment. It should eventually call - // self.evaluate_const like projected consts do in consider_impl_candidate in - // normalizes_to/mod.rs. However, how generic args are represented for IACs is up in - // the air right now. - // Will self.evaluate_const eventually take the inherent_args or the impl_args form - // of args? It might be either. - panic!("References to inherent associated consts should have been blocked"); + ty::AliasTermKind::InherentConstImpl { .. } => { + let term = ty::AliasTerm::new_from_args(cx, inherent_kind, inherent_args); + // NOTE: we intentionally pass in the `InherentConstImpl` form as the term to + // instantiate to upon too-generic CTFE failure, as we ought to consistently compare + // identities via `InherentConstImpl` rather than `InherentConstSelf`. + return self.evaluate_const_and_instantiate_projection_term( + goal.param_env, + term, + goal.predicate.term, + term.expect_ct(), + ); } kind => panic!("expected inherent alias, found {kind:?}"), }; @@ -84,4 +76,43 @@ where self.eq(goal.param_env, goal.predicate.term, normalized)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + + fn convert_inherent_self_to_impl( + &mut self, + param_env: I::ParamEnv, + term: ty::AliasTerm, + ) -> Result<(ty::AliasTermKind, I::GenericArgs), NoSolutionOrRerunNonErased> { + match term.kind { + ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConstSelf { .. } => { + let cx = self.cx(); + let def_id = term.expect_inherent_def_id(); + let impl_def_id = cx.inherent_alias_term_parent(def_id); + let impl_args = self.fresh_args_for_item(impl_def_id.into()); + + // Equate impl header and add impl where clauses + self.eq( + param_env, + term.self_ty(), + cx.type_of(impl_def_id.into()).instantiate(cx, impl_args).skip_norm_wip(), + )?; + + // Equate IAT with the RHS of the project goal + let inherent_args = term.rebase_inherent_args_onto_impl(impl_args, cx); + + let kind = match term.kind { + ty::AliasTermKind::InherentTy { def_id } => { + ty::AliasTermKind::InherentTy { def_id } + } + ty::AliasTermKind::InherentConstSelf { def_id } => { + ty::AliasTermKind::InherentConstImpl { def_id } + } + _ => unreachable!(), + }; + + Ok((kind, inherent_args)) + } + ty::AliasTermKind::InherentConstImpl { .. } => Ok((term.kind, term.args)), + kind => panic!("expected inherent alias, found {kind:?}"), + } + } } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs index 6ec82aefb523f..db326e6d736a4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs @@ -27,7 +27,9 @@ where ty::AliasTermKind::ProjectionTy { .. } | ty::AliasTermKind::ProjectionConst { .. } => { self.normalize_associated_term(goal) } - ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConst { .. } => { + ty::AliasTermKind::InherentTy { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } => { self.normalize_inherent_associated_term(goal) } ty::AliasTermKind::OpaqueTy { .. } => self.normalize_opaque_type(goal), diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index b0271a1ae993a..cd7f422fb72f3 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -13,7 +13,7 @@ use rustc_ast::{AttrStyle, MetaItemKind, ast}; use rustc_attr_parsing::AttributeParser; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, msg}; -use rustc_feature::BUILTIN_ATTRIBUTE_MAP; +use rustc_feature::BUILTIN_ATTRIBUTE_SET; use rustc_hir::attrs::diagnostic::Directive; use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::attrs::{ @@ -146,7 +146,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid, ..] => {} [name, rest @ ..] => { - if let Some(_) = BUILTIN_ATTRIBUTE_MAP.get(name) { + if BUILTIN_ATTRIBUTE_SET.contains(name) { if rest.len() > 0 && AttributeParser::is_parsed_attribute(slice::from_ref(name)) { diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index e142cb26447a8..17ce015d4ce10 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -60,7 +60,8 @@ impl<'tcx> Stable<'tcx> for ty::AliasTerm<'tcx> { | ty::AliasTermKind::AnonConst { def_id } | ty::AliasTermKind::ProjectionConst { def_id } | ty::AliasTermKind::FreeConst { def_id } - | ty::AliasTermKind::InherentConst { def_id } => def_id, + | ty::AliasTermKind::InherentConstSelf { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => def_id, }; crate::ty::AliasTerm { def_id: tables.alias_def(def_id), args: args.stable(tables, cx) } } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 8a44589f5052c..2019f7e15dc70 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -250,6 +250,7 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc tcx, assoc_item.def_id, super_trait_ref.args, + ty::AliasConstInherentArgsKind::WithSelf, ); let term = tcx.normalize_erasing_regions( ty::TypingEnv::fully_monomorphized(), diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index cf08d3e858ec5..5ed41ac456031 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -749,7 +749,8 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // logic sometimes passing identity-substituted impl headers. ty::ConstKind::Alias(_, ty::AliasConst { kind, args, .. }) => match kind { ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => { return self.print_def_path(def_id, args); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 1210a3ef57e32..34df03e2584e6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1613,7 +1613,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::AliasTermKind::AnonConst { def_id } => def_id.into(), ty::AliasTermKind::ProjectionConst { def_id } => def_id.into(), ty::AliasTermKind::FreeConst { def_id } => def_id.into(), - ty::AliasTermKind::InherentConst { def_id } => def_id.into(), + ty::AliasTermKind::InherentConstSelf { def_id } => def_id.into(), + ty::AliasTermKind::InherentConstImpl { def_id } => def_id.into(), }; (false, Mismatch::Fixed(self.tcx.def_descr(def_id))) } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index bca336c2a0449..ddbb56affcff1 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -720,7 +720,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { && matches!( a.kind, ty::AliasConstKind::Projection { .. } - | ty::AliasConstKind::Inherent { .. } + | ty::AliasConstKind::InherentSelf { .. } + | ty::AliasConstKind::InherentImpl { .. } ) => { if let Ok(new_obligations) = infcx diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index f00b300c7e971..0d22ca4973511 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -491,7 +491,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx ty::AliasConstKind::Projection { .. } => { self.normalize_trait_projection(alias_const.into()).expect_const() } - ty::AliasConstKind::Inherent { .. } => { + ty::AliasConstKind::InherentSelf { .. } | ty::AliasConstKind::InherentImpl { .. } => { self.normalize_inherent_projection(alias_const.into()).expect_const() } ty::AliasConstKind::Free { .. } => { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index eaaf082b105c2..9d0daa3a8672b 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -470,18 +470,7 @@ fn normalize_to_error<'a, 'tcx>( depth: usize, ) -> NormalizedTerm<'tcx> { let trait_ref = ty::Binder::dummy(projection_term.trait_ref(selcx.tcx())); - let new_value = match projection_term.kind { - ty::AliasTermKind::ProjectionTy { .. } - | ty::AliasTermKind::InherentTy { .. } - | ty::AliasTermKind::OpaqueTy { .. } - | ty::AliasTermKind::FreeTy { .. } => selcx.infcx.next_ty_var(cause.span).into(), - ty::AliasTermKind::FreeConst { .. } - | ty::AliasTermKind::InherentConst { .. } - | ty::AliasTermKind::AnonConst { .. } - | ty::AliasTermKind::ProjectionConst { .. } => { - selcx.infcx.next_const_var(cause.span).into() - } - }; + let new_value = selcx.infcx.next_term_var_of_alias_kind(projection_term, cause.span); let mut obligations = PredicateObligations::new(); obligations.push(Obligation { cause, @@ -608,7 +597,13 @@ pub fn compute_inherent_assoc_term_args<'a, 'b, 'tcx>( ) -> ty::GenericArgsRef<'tcx> { let tcx = selcx.tcx(); - let alias_def_id = alias_term.expect_inherent_def_id(); + let alias_def_id = match alias_term.kind { + ty::AliasTermKind::InherentTy { def_id } => def_id, + ty::AliasTermKind::InherentConstSelf { def_id } => def_id, + ty::AliasTermKind::InherentConstImpl { .. } => return alias_term.args, + kind => panic!("expected inherent alias, found {kind:?}"), + }; + let impl_def_id = tcx.parent(alias_def_id); let impl_args = selcx.infcx.fresh_args_for_item(cause.span, impl_def_id); @@ -2101,13 +2096,13 @@ fn confirm_impl_candidate<'cx, 'tcx>( let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args); let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_term.defining_node); - let term = if obligation.predicate.kind.is_type() { - tcx.type_of(assoc_term.item.def_id).map_bound(|ty| ty.into()) + let term_kind = if obligation.predicate.kind.is_type() { + ty::AliasTermKind::ProjectionTy { def_id: assoc_term.item.def_id } } else { - tcx.const_of_item(assoc_term.item.def_id).map_bound(|ct| ct.into()) + ty::AliasTermKind::ProjectionConst { def_id: assoc_term.item.def_id } }; - let progress = if !tcx.check_args_compatible(assoc_term.item.def_id, args) { + let progress = if !tcx.check_alias_term_args_compatible(term_kind, args) { let msg = "impl item and trait item have different parameters"; let span = obligation.cause.span; let err = if obligation.predicate.kind.is_type() { @@ -2117,6 +2112,12 @@ fn confirm_impl_candidate<'cx, 'tcx>( }; Progress { term: ty::Unnormalized::dummy(err), obligations: nested } } else { + let term = if obligation.predicate.kind.is_type() { + tcx.type_of(assoc_term.item.def_id).map_bound(|ty| ty.into()) + } else { + tcx.const_of_item(assoc_term.item.def_id).map_bound(|ct| ct.into()) + }; + assoc_term_own_obligations(selcx, obligation, &mut nested); let instantiated_term = term.instantiate(tcx, args); let term_for_obligation = instantiated_term.skip_norm_wip(); diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 489e4f7a93d53..96e41f89be573 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -331,7 +331,9 @@ impl<'a, 'tcx> QueryNormalizer<'a, 'tcx> { ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } => { tcx.normalize_canonicalized_free_alias(c_term) } - ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConst { .. } => { + ty::AliasTermKind::InherentTy { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } => { tcx.normalize_canonicalized_inherent_projection(c_term) } kind @ (ty::AliasTermKind::OpaqueTy { .. } | ty::AliasTermKind::AnonConst { .. }) => { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 239cbc5aa7622..b4ab3b9729fd3 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -1,4 +1,3 @@ -use rustc_hir::def::DefKind; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::traits::Obligation; use rustc_middle::traits::query::NoSolution; @@ -91,27 +90,6 @@ fn relate_mir_and_user_args<'tcx>( let tcx = ocx.infcx.tcx; let cause = ObligationCause::dummy_with_span(span); - // For IACs, the user args are in the format [SelfTy, GAT_args...] but type_of expects [impl_args..., GAT_args...]. - // We need to infer the impl args by equating the impl's self type with the user-provided self type. - let is_inherent_assoc_const = matches!(tcx.def_kind(def_id), DefKind::AssocConst { .. }) - && tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false } - && tcx.is_type_const(def_id); - - let args = if is_inherent_assoc_const { - let impl_def_id = tcx.parent(def_id); - let impl_args = ocx.infcx.fresh_args_for_item(span, impl_def_id); - let impl_self_ty = - ocx.normalize(&cause, param_env, tcx.type_of(impl_def_id).instantiate(tcx, impl_args)); - let user_self_ty = - ocx.normalize(&cause, param_env, Unnormalized::new_wip(args[0].expect_ty())); - ocx.eq(&cause, param_env, impl_self_ty, user_self_ty)?; - - let gat_args = &args[1..]; - tcx.mk_args_from_iter(impl_args.iter().chain(gat_args.iter().copied())) - } else { - args - }; - let ty = tcx.type_of(def_id).instantiate(tcx, args); let ty = ocx.normalize(&cause, param_env, ty); debug!("relate_type_and_user_type: ty of def-id is {:?}", ty); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index f1eaa50797c49..a2785a7ca75dc 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -876,7 +876,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { && matches!( a.kind, ty::AliasConstKind::Projection { .. } - | ty::AliasConstKind::Inherent { .. } + | ty::AliasConstKind::InherentSelf { .. } + | ty::AliasConstKind::InherentImpl { .. } ) => { if let Ok(InferOk { obligations, value: () }) = self diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 5427d14c55af9..dc29b6311cc7e 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1095,10 +1095,14 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } match alias_const.kind { - ty::AliasConstKind::Inherent { .. } => { + ty::AliasConstKind::InherentSelf { .. } => { self.add_wf_preds_for_inherent_projection(alias_const.into()); return; // Subtree is handled by above function } + // please ping khyperia and/or BoxyUwU if this `bug!` fires + ty::AliasConstKind::InherentImpl { .. } => bug!( + "This ought to be unreachable, the entrypoints of WF should still have InherentSelf-form alias consts." + ), ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => { diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index cd35423c5ef14..d48438819040a 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -70,8 +70,15 @@ fn recurse_build<'tcx>( } &ExprKind::ZstLiteral { user_ty: _ } => ty::Const::zero_sized(tcx, node.ty), &ExprKind::NamedConst { def_id, args, user_ty: _ } => { - let uneval = - ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, def_id), args); + let uneval = ty::AliasConst::new( + tcx, + ty::AliasConstKind::new_from_def_id( + tcx, + def_id, + ty::AliasConstInherentArgsKind::Impl, + ), + args, + ); ty::Const::new_alias(tcx, ty::IsRigid::No, uneval) } ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param), diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 29c65974d8b28..26a4edccd0134 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -73,13 +73,7 @@ impl AliasConst { #[inline] pub fn new(interner: I, kind: AliasConstKind, args: I::GenericArgs) -> AliasConst { if cfg!(debug_assertions) { - let def_id = match kind { - ty::AliasConstKind::Projection { def_id } => def_id.into(), - ty::AliasConstKind::Inherent { def_id } => def_id.into(), - ty::AliasConstKind::Free { def_id } => def_id.into(), - ty::AliasConstKind::Anon { def_id } => def_id.into(), - }; - interner.debug_assert_args_compatible(def_id, args); + interner.debug_assert_alias_term_args_compatible(kind.into(), args); } AliasConst { kind, args, _use_alias_new_instead: () } } @@ -87,7 +81,12 @@ impl AliasConst { pub fn type_of(self, interner: I) -> ty::Unnormalized { let def_id = match self.kind { ty::AliasConstKind::Projection { def_id } => def_id.into(), - ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::InherentSelf { .. } => { + panic!( + "AliasConst::type_of got InherentSelf - args should always be InherentImpl at this point" + ) + } + ty::AliasConstKind::InherentImpl { def_id } => def_id.into(), ty::AliasConstKind::Free { def_id } => def_id.into(), ty::AliasConstKind::Anon { def_id } => def_id.into(), }; @@ -107,23 +106,65 @@ impl AliasConst { pub enum AliasConstKind { /// A projection `::AssocConst` Projection { def_id: I::TraitAssocConstId }, - /// An associated constant in an inherent `impl` - Inherent { def_id: I::InherentAssocConstId }, + /// An associated const in an inherent `impl`. + /// + /// The generic args are in "Self form", i.e. + /// there is a single `Self` type parameter, followed by any GAT args on the inherent const + /// itself. + /// + /// The "impl form" args can be obtained by generating fresh vars for each of the impl params, + /// instantiating the impl block's Self type with the fresh vars, equating the resulting type + /// with the `Self` generic argument, and using the result of what the fresh vars resolved to as + /// the "impl form" args. Doing so without considering the extra predicates generated by the + /// equate is a lossy operation, consider the following impl block: + /// + /// ```rust,ignore (illustrative) + /// impl Struct<'static, T> { + /// const ASSOC: () = (); + /// } + /// ``` + /// + /// If we have `Struct::<'a, u32>::Assoc`, the Self args form would be `[Struct<'a, u32>, + /// usize]`. The "impl form" args would be `[u32, usize]`, with an extra constraint generated + /// that `'a == 'static`. Disregarding this extra constraint would be wrong. + /// + /// Hence, when HIR lowering wants to construct an inherent alias, it must use the "Self form" + /// to let the trait solver do the equate and consider additional constraints. + /// + /// FIXME(inherent_associated_types): This ideally ought be a list of candidate DefIds that a + /// path could resolve to, then the trait solver does the above-written routine to figure out + /// which exact impl to use. `InherentSelf` could be conceptually be thought of as corresponding + /// to `Projection` where the def_id is a trait, and `InherentImpl` is `Projection` where the + /// def_id is an impl. + InherentSelf { def_id: I::InherentAssocConstId }, + /// An associated const in an inherent `impl`. See [`Self::InherentSelf`] for a description on + /// the difference between `InherentSelf` and `InherentImpl`. + InherentImpl { def_id: I::InherentAssocConstId }, /// A free constant, outside an impl block. Free { def_id: I::FreeConstAliasId }, /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`. Anon { def_id: I::AnonConstId }, } +pub enum AliasConstInherentArgsKind { + WithSelf, + Impl, +} + impl AliasConstKind { - pub fn new_from_def_id(interner: I, def_id: I::DefId) -> Self { - interner.alias_const_kind_from_def_id(def_id) + pub fn new_from_def_id( + interner: I, + def_id: I::DefId, + inherent_args: AliasConstInherentArgsKind, + ) -> Self { + interner.alias_const_kind_from_def_id(def_id, inherent_args) } pub fn is_type_const(self, interner: I) -> bool { match self { AliasConstKind::Projection { def_id } => interner.is_type_const(def_id.into()), - AliasConstKind::Inherent { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::InherentSelf { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::InherentImpl { def_id } => interner.is_type_const(def_id.into()), AliasConstKind::Free { def_id } => interner.is_type_const(def_id.into()), AliasConstKind::Anon { def_id } => interner.is_type_const(def_id.into()), } @@ -132,7 +173,8 @@ impl AliasConstKind { pub fn def_span(self, interner: I) -> I::Span { match self { AliasConstKind::Projection { def_id } => interner.def_span(def_id.into()), - AliasConstKind::Inherent { def_id } => interner.def_span(def_id.into()), + AliasConstKind::InherentSelf { def_id } => interner.def_span(def_id.into()), + AliasConstKind::InherentImpl { def_id } => interner.def_span(def_id.into()), AliasConstKind::Free { def_id } => interner.def_span(def_id.into()), AliasConstKind::Anon { def_id } => interner.def_span(def_id.into()), } @@ -141,7 +183,8 @@ impl AliasConstKind { pub fn opt_def_id(self) -> Option { match self { AliasConstKind::Projection { def_id } => Some(def_id.into()), - AliasConstKind::Inherent { def_id } => Some(def_id.into()), + AliasConstKind::InherentSelf { def_id } => Some(def_id.into()), + AliasConstKind::InherentImpl { def_id } => Some(def_id.into()), AliasConstKind::Free { def_id } => Some(def_id.into()), AliasConstKind::Anon { def_id } => Some(def_id.into()), } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index d230791304527..7060bae7d12ec 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -21,8 +21,8 @@ use crate::solve::{ }; use crate::visit::{Flags, TypeVisitable}; use crate::{ - self as ty, BoundRegion, BoundVar, CanonicalParamEnvCache, DebruijnIndex, Region, RegionKind, - TraitRef, search_graph, + self as ty, AliasTermKind, BoundRegion, BoundVar, CanonicalParamEnvCache, DebruijnIndex, + Region, RegionKind, TraitRef, search_graph, }; /// The central trait in the shared abstraction layer, specifying all implementation-specific @@ -275,10 +275,18 @@ pub trait Interner: type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef; - fn alias_const_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasConstKind; + fn alias_const_kind_from_def_id( + self, + def_id: Self::DefId, + inherent_args: ty::AliasConstInherentArgsKind, + ) -> ty::AliasConstKind; // FIXME: remove in favor of explicit construction - fn alias_term_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasTermKind; + fn alias_term_kind_from_def_id( + self, + def_id: Self::DefId, + inherent_args: ty::AliasConstInherentArgsKind, + ) -> ty::AliasTermKind; fn trait_ref_and_own_args_for_alias( self, @@ -293,9 +301,18 @@ pub trait Interner: I: Iterator, T: CollectAndApply; - fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool; + fn check_alias_term_args_compatible( + self, + term_kind: AliasTermKind, + args: Self::GenericArgs, + ) -> bool; fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs); + fn debug_assert_alias_term_args_compatible( + self, + term_kind: AliasTermKind, + args: Self::GenericArgs, + ); /// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection` /// are compatible with the `DefId`. diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 7d281663d5033..7ad23d3e5432a 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -502,7 +502,10 @@ impl ExistentialProjection { ProjectionClause { projection_term: ty::AliasTerm::new( interner, - interner.alias_term_kind_from_def_id(self.def_id.into()), + interner.alias_term_kind_from_def_id( + self.def_id.into(), + ty::AliasConstInherentArgsKind::WithSelf, + ), [self_ty.into()].iter().chain(self.args.iter()), ), term: self.term, diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 98d251c6f1d64..f6491bac642e3 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -262,7 +262,8 @@ impl Relate for ty::AliasTerm { | ty::AliasTermKind::FreeConst { .. } | ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::InherentTy { .. } - | ty::AliasTermKind::InherentConst { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::AnonConst { .. } | ty::AliasTermKind::ProjectionConst { .. } => { relate_args_invariantly(relation, a.args, b.args)? @@ -281,8 +282,14 @@ impl Relate for ty::ExistentialProjection { ) -> RelateResult> { if a.def_id != b.def_id { Err(TypeError::ProjectionMismatched(ExpectedFound::new( - relation.cx().alias_term_kind_from_def_id(a.def_id.into()), - relation.cx().alias_term_kind_from_def_id(b.def_id.into()), + relation.cx().alias_term_kind_from_def_id( + a.def_id.into(), + ty::AliasConstInherentArgsKind::WithSelf, + ), + relation.cx().alias_term_kind_from_def_id( + b.def_id.into(), + ty::AliasConstInherentArgsKind::WithSelf, + ), ))) } else { let term = relation.relate_with_variance( diff --git a/compiler/rustc_type_ir/src/term_kind.rs b/compiler/rustc_type_ir/src/term_kind.rs index aed634d4f3a21..bb4ebf054d263 100644 --- a/compiler/rustc_type_ir/src/term_kind.rs +++ b/compiler/rustc_type_ir/src/term_kind.rs @@ -66,8 +66,12 @@ pub enum AliasTermKind { ProjectionConst { def_id: I::TraitAssocConstId }, /// A top level const item not part of a trait or impl. FreeConst { def_id: I::FreeConstAliasId }, - /// An associated const in an inherent `impl` - InherentConst { def_id: I::InherentAssocConstId }, + /// An associated const in an inherent `impl`. See [`ty::AliasConstKind::InherentSelf`] for a + /// description on the difference between `InherentConstSelf` and `InherentConstImpl`. + InherentConstSelf { def_id: I::InherentAssocConstId }, + /// An associated const in an inherent `impl`. See [`ty::AliasConstKind::InherentSelf`] for a + /// description on the difference between `InherentConstSelf` and `InherentConstImpl`. + InherentConstImpl { def_id: I::InherentAssocConstId }, } impl AliasTermKind { @@ -76,7 +80,9 @@ impl AliasTermKind { AliasTermKind::ProjectionTy { .. } => "associated type", AliasTermKind::ProjectionConst { .. } => "associated const", AliasTermKind::InherentTy { .. } => "inherent associated type", - AliasTermKind::InherentConst { .. } => "inherent associated const", + AliasTermKind::InherentConstSelf { .. } | AliasTermKind::InherentConstImpl { .. } => { + "inherent associated const" + } AliasTermKind::OpaqueTy { .. } => "opaque type", AliasTermKind::FreeTy { .. } => "type alias", AliasTermKind::FreeConst { .. } => "const alias", @@ -93,7 +99,8 @@ impl AliasTermKind { AliasTermKind::AnonConst { .. } | AliasTermKind::ProjectionConst { .. } - | AliasTermKind::InherentConst { .. } + | AliasTermKind::InherentConstSelf { .. } + | AliasTermKind::InherentConstImpl { .. } | AliasTermKind::FreeConst { .. } => false, } } @@ -106,7 +113,8 @@ impl AliasTermKind { | AliasTermKind::FreeTy { .. } | AliasTermKind::AnonConst { .. } | AliasTermKind::FreeConst { .. } - | AliasTermKind::InherentConst { .. } => false, + | AliasTermKind::InherentConstSelf { .. } + | AliasTermKind::InherentConstImpl { .. } => false, } } } @@ -126,7 +134,12 @@ impl From> for AliasTermKind { fn from(value: ty::AliasConstKind) -> Self { match value { ty::AliasConstKind::Projection { def_id } => AliasTermKind::ProjectionConst { def_id }, - ty::AliasConstKind::Inherent { def_id } => AliasTermKind::InherentConst { def_id }, + ty::AliasConstKind::InherentSelf { def_id } => { + AliasTermKind::InherentConstSelf { def_id } + } + ty::AliasConstKind::InherentImpl { def_id } => { + AliasTermKind::InherentConstImpl { def_id } + } ty::AliasConstKind::Free { def_id } => AliasTermKind::FreeConst { def_id }, ty::AliasConstKind::Anon { def_id } => AliasTermKind::AnonConst { def_id }, } @@ -140,17 +153,7 @@ impl AliasTerm { args: I::GenericArgs, ) -> AliasTerm { if cfg!(debug_assertions) { - let def_id = match kind { - AliasTermKind::ProjectionTy { def_id } => def_id.into(), - AliasTermKind::InherentTy { def_id } => def_id.into(), - AliasTermKind::OpaqueTy { def_id } => def_id.into(), - AliasTermKind::FreeTy { def_id } => def_id.into(), - AliasTermKind::AnonConst { def_id } => def_id.into(), - AliasTermKind::ProjectionConst { def_id } => def_id.into(), - AliasTermKind::FreeConst { def_id } => def_id.into(), - AliasTermKind::InherentConst { def_id } => def_id.into(), - }; - interner.debug_assert_args_compatible(def_id, args); + interner.debug_assert_alias_term_args_compatible(kind, args); } AliasTerm { kind, args, _use_alias_new_instead: () } } @@ -164,8 +167,13 @@ impl AliasTerm { Self::new_from_args(interner, kind, args) } - pub fn new_from_def_id(interner: I, def_id: I::DefId, args: I::GenericArgs) -> AliasTerm { - let kind = interner.alias_term_kind_from_def_id(def_id); + pub fn new_from_def_id( + interner: I, + def_id: I::DefId, + args: I::GenericArgs, + inherent_args: ty::AliasConstInherentArgsKind, + ) -> AliasTerm { + let kind = interner.alias_term_kind_from_def_id(def_id, inherent_args); Self::new_from_args(interner, kind, args) } @@ -175,7 +183,8 @@ impl AliasTerm { AliasTermKind::InherentTy { def_id } => ty::AliasTyKind::Inherent { def_id }, AliasTermKind::OpaqueTy { def_id } => ty::AliasTyKind::Opaque { def_id }, AliasTermKind::FreeTy { def_id } => ty::AliasTyKind::Free { def_id }, - kind @ (AliasTermKind::InherentConst { .. } + kind @ (AliasTermKind::InherentConstSelf { .. } + | AliasTermKind::InherentConstImpl { .. } | AliasTermKind::FreeConst { .. } | AliasTermKind::AnonConst { .. } | AliasTermKind::ProjectionConst { .. }) => { @@ -187,7 +196,12 @@ impl AliasTerm { pub fn expect_ct(self) -> ty::AliasConst { let kind = match self.kind { - AliasTermKind::InherentConst { def_id } => ty::AliasConstKind::Inherent { def_id }, + AliasTermKind::InherentConstSelf { def_id } => { + ty::AliasConstKind::InherentSelf { def_id } + } + AliasTermKind::InherentConstImpl { def_id } => { + ty::AliasConstKind::InherentImpl { def_id } + } AliasTermKind::FreeConst { def_id } => ty::AliasConstKind::Free { def_id }, AliasTermKind::AnonConst { def_id } => ty::AliasConstKind::Anon { def_id }, AliasTermKind::ProjectionConst { def_id } => ty::AliasConstKind::Projection { def_id }, @@ -212,8 +226,11 @@ impl AliasTerm { }; match self.kind { AliasTermKind::FreeConst { def_id } => alias_const(ty::AliasConstKind::Free { def_id }), - AliasTermKind::InherentConst { def_id } => { - alias_const(ty::AliasConstKind::Inherent { def_id }) + AliasTermKind::InherentConstSelf { def_id } => { + alias_const(ty::AliasConstKind::InherentSelf { def_id }) + } + AliasTermKind::InherentConstImpl { def_id } => { + alias_const(ty::AliasConstKind::InherentImpl { def_id }) } AliasTermKind::AnonConst { def_id } => alias_const(ty::AliasConstKind::Anon { def_id }), AliasTermKind::ProjectionConst { def_id } => { @@ -305,7 +322,8 @@ impl AliasTerm { pub fn expect_inherent_def_id(self) -> I::InherentAssocTermId { match self.kind { AliasTermKind::InherentTy { def_id } => def_id.into(), - AliasTermKind::InherentConst { def_id } => def_id.into(), + AliasTermKind::InherentConstSelf { def_id } => def_id.into(), + AliasTermKind::InherentConstImpl { def_id } => def_id.into(), kind => panic!("expected inherent alias, found {kind:?}"), } } @@ -327,7 +345,7 @@ impl AliasTerm { ) -> I::GenericArgs { debug_assert!(matches!( self.kind, - AliasTermKind::InherentTy { .. } | AliasTermKind::InherentConst { .. } + AliasTermKind::InherentTy { .. } | AliasTermKind::InherentConstSelf { .. } )); interner.mk_args_from_iter(impl_args.iter().chain(self.args.iter().skip(1))) } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 0a9eaf360e3ef..abedc4d079e37 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -489,13 +489,7 @@ impl fmt::Debug for TyKind { impl AliasTy { pub fn new_from_args(interner: I, kind: AliasTyKind, args: I::GenericArgs) -> AliasTy { if cfg!(debug_assertions) { - let def_id = match kind { - AliasTyKind::Projection { def_id } => def_id.into(), - AliasTyKind::Inherent { def_id } => def_id.into(), - AliasTyKind::Opaque { def_id } => def_id.into(), - AliasTyKind::Free { def_id } => def_id.into(), - }; - interner.debug_assert_args_compatible(def_id, args); + interner.debug_assert_alias_term_args_compatible(kind.into(), args); } AliasTy { kind, args, _use_alias_new_instead: () } } @@ -557,7 +551,10 @@ impl ProjectionAliasTy { kind: I::TraitAssocTyId, args: I::GenericArgs, ) -> Self { - interner.debug_assert_args_compatible(kind.into(), args); + interner.debug_assert_alias_term_args_compatible( + ty::AliasTermKind::ProjectionTy { def_id: kind }, + args, + ); Self { kind, args, _use_alias_new_instead: () } } @@ -628,7 +625,10 @@ impl InherentAliasTy { kind: I::InherentAssocTyId, args: I::GenericArgs, ) -> Self { - interner.debug_assert_args_compatible(kind.into(), args); + interner.debug_assert_alias_term_args_compatible( + ty::AliasTermKind::InherentTy { def_id: kind }, + args, + ); Self { kind, args, _use_alias_new_instead: () } } @@ -643,7 +643,10 @@ impl InherentAliasTy { impl OpaqueAliasTy { pub fn new_opaque_from_args(interner: I, kind: I::OpaqueTyId, args: I::GenericArgs) -> Self { - interner.debug_assert_args_compatible(kind.into(), args); + interner.debug_assert_alias_term_args_compatible( + ty::AliasTermKind::OpaqueTy { def_id: kind }, + args, + ); Self { kind, args, _use_alias_new_instead: () } } @@ -658,7 +661,10 @@ impl OpaqueAliasTy { impl FreeAliasTy { pub fn new_free_from_args(interner: I, kind: I::FreeTyAliasId, args: I::GenericArgs) -> Self { - interner.debug_assert_args_compatible(kind.into(), args); + interner.debug_assert_alias_term_args_compatible( + ty::AliasTermKind::FreeTy { def_id: kind }, + args, + ); Self { kind, args, _use_alias_new_instead: () } } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 7d5c5176c952f..473f01660bdb4 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -721,14 +721,12 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(smart_pointer_try_map)] - /// /// let b = Box::new(7); /// let new = Box::map(b, |i| i + 7); /// assert_eq!(*new, 14); /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "smart_pointer_try_map", issue = "144419")] + #[stable(feature = "smart_pointer_map", since = "CURRENT_RUSTC_VERSION")] pub fn map(this: Self, f: impl FnOnce(T) -> U) -> Box { let (value, allocation) = Box::take(this); let (raw, alloc) = Box::into_non_null_with_allocator(allocation); diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index b4822d98bb45a..1e859b38c9dd7 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1053,8 +1053,6 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(smart_pointer_try_map)] - /// /// use std::rc::Rc; /// /// let r = Rc::new(7); @@ -1062,7 +1060,7 @@ impl Rc { /// assert_eq!(*new, 14); /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "smart_pointer_try_map", issue = "144419")] + #[stable(feature = "smart_pointer_map", since = "CURRENT_RUSTC_VERSION")] pub fn map(this: Self, f: impl FnOnce(&T) -> U) -> Rc { if size_of::() == size_of::() && align_of::() == align_of::() @@ -4326,7 +4324,6 @@ impl UniqueRc { /// # Examples /// /// ``` - /// #![feature(smart_pointer_try_map)] /// #![feature(unique_rc_arc)] /// /// use std::rc::UniqueRc; @@ -4336,7 +4333,7 @@ impl UniqueRc { /// assert_eq!(*new, 14); /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "smart_pointer_try_map", issue = "144419")] + #[unstable(feature = "unique_rc_arc", issue = "112566")] pub fn map(this: Self, f: impl FnOnce(T) -> U) -> UniqueRc { if size_of::() == size_of::() && align_of::() == align_of::() diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 27192cf686c12..ae15f6d69986b 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1217,8 +1217,6 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(smart_pointer_try_map)] - /// /// use std::sync::Arc; /// /// let r = Arc::new(7); @@ -1226,7 +1224,7 @@ impl Arc { /// assert_eq!(*new, 14); /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "smart_pointer_try_map", issue = "144419")] + #[stable(feature = "smart_pointer_map", since = "CURRENT_RUSTC_VERSION")] pub fn map(this: Self, f: impl FnOnce(&T) -> U) -> Arc { if size_of::() == size_of::() && align_of::() == align_of::() @@ -4796,7 +4794,6 @@ impl UniqueArc { /// # Examples /// /// ``` - /// #![feature(smart_pointer_try_map)] /// #![feature(unique_rc_arc)] /// /// use std::sync::UniqueArc; @@ -4806,7 +4803,7 @@ impl UniqueArc { /// assert_eq!(*new, 14); /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "smart_pointer_try_map", issue = "144419")] + #[unstable(feature = "unique_rc_arc", issue = "112566")] pub fn map(this: Self, f: impl FnOnce(T) -> U) -> UniqueArc { if size_of::() == size_of::() && align_of::() == align_of::() diff --git a/library/compiler-builtins/.github/workflows/main.yaml b/library/compiler-builtins/.github/workflows/main.yaml index 2ded0f6177d7b..51d0734a4c4df 100644 --- a/library/compiler-builtins/.github/workflows/main.yaml +++ b/library/compiler-builtins/.github/workflows/main.yaml @@ -11,11 +11,12 @@ concurrency: env: CARGO_TERM_COLOR: always + CARGO_TERM_VERBOSE: true LIBM_BUILD_VERBOSE: true RUSTDOCFLAGS: -Dwarnings - RUSTFLAGS: -Dwarnings + RUSTFLAGS: -Dwarnings -Dlinker_messages RUST_BACKTRACE: full - BENCHMARK_RUSTC: nightly-2026-08-05 # Pin the toolchain for reproducable results + BENCHMARK_RUSTC: nightly-2026-08-19 # Pin the toolchain for reproducable results defaults: run: @@ -91,6 +92,8 @@ jobs: # os: ["self-hosted", "linux", "riscv64"] - target: riscv64gc-unknown-linux-gnu os: ubuntu-26.04 + - target: s390x-unknown-linux-gnu + os: self-hosted-linux-s390x-resolute-large-rust # resolute == ubuntu-26.04 - target: s390x-unknown-linux-gnu os: ubuntu-24.04-s390x - target: thumbv6m-none-eabi @@ -111,9 +114,10 @@ jobs: os: windows-2025-vs2026 - target: x86_64-pc-windows-msvc os: windows-2025-vs2026 - - target: i686-pc-windows-gnu - os: windows-2025-vs2026 - channel: nightly-i686-gnu + # FIXME(rust-lang/compiler-builtins#1306): disabled due to broken environment + # - target: i686-pc-windows-gnu + # os: windows-2025-vs2026 + # channel: nightly-i686-gnu - target: x86_64-pc-windows-gnu os: windows-2025-vs2026 channel: nightly-x86_64-gnu @@ -132,26 +136,20 @@ jobs: lscpu || (sysctl -a | grep cpu) || true echo "home: ${HOME:-not found}" pwd - - # Native ppc and s390x runners don't have rustup by default - - name: Install rustup - if: matrix.os == 'ubuntu-26.04-ppc64le' || matrix.os == 'ubuntu-26.04-s390x' - run: sudo apt-get update && sudo apt-get install -y rustup - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: { persist-credentials: false } - - name: Install Rust (rustup) - run: | - channel="nightly" - # Account for channels that have required components (MinGW) - [ -n "$JOB_CHANNEL" ] && channel="$JOB_CHANNEL" - rustup update "$channel" --no-self-update - rustup default "$channel" - rustup target add "$JOB_TARGET" + + - name: Set up dependencies and Rust + run: ./ci/install-test-deps.sh "$JOB_TARGET" "$JOB_CHANNEL" "$RUN_IN_DOCKER" - uses: taiki-e/install-action@7f4eb899022d8fe70b20c4f3de697aa85c309026 # v2.85.11 + continue-on-error: true with: tool: nextest@0.9.131 + # On platforms without prebuilts, nextest can be installed system-wide + # or omitted. Building it probably takes longer than running tests + # without it + fallback: none - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: @@ -172,7 +170,7 @@ jobs: uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: compiler-rt - key: ${{ runner.os }}-compiler-rt-${{ hashFiles('ci/download-compiler-rt.sh', 'ci/compiler-rt-patches') }} + key: ${{ runner.os }}-compiler-rt-${{ hashFiles('ci/download-compiler-rt.sh') }} - name: Download compiler-rt reference sources if: steps.cache-compiler-rt.outputs.cache-hit != 'true' run: ./ci/download-compiler-rt.sh @@ -345,7 +343,7 @@ jobs: msrv: name: Check libm MSRV runs-on: ubuntu-26.04 - timeout-minutes: 10 + timeout-minutes: 20 env: RUSTFLAGS: # No need to check warnings on old MSRV, unset `-Dwarnings` steps: @@ -358,7 +356,7 @@ jobs: rustup update "$msrv" --no-self-update && rustup default "$msrv" - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 - run: | - # FIXME(msrv): Remove the workspace Cargo.toml so 1.63 cargo doesn't see + # FIXME(msrv): Remove the workspace Cargo.toml so MSRV cargo doesn't see # `edition = "2024"` and get spooked. rm Cargo.toml cargo build --manifest-path libm/Cargo.toml diff --git a/library/compiler-builtins/builtins-test/Cargo.toml b/library/compiler-builtins/builtins-test/Cargo.toml index f1a5be415675d..b3227b5751164 100644 --- a/library/compiler-builtins/builtins-test/Cargo.toml +++ b/library/compiler-builtins/builtins-test/Cargo.toml @@ -37,6 +37,10 @@ icount = ["dep:gungraun"] benchmarking-reports = ["walltime", "criterion/plotters", "criterion/html_reports"] walltime = ["dep:criterion"] +[lints.clippy] +# This sometimes reads better +needless-range-loop = "allow" + [[bench]] name = "float_add" harness = false diff --git a/library/compiler-builtins/builtins-test/benches/mem.rs b/library/compiler-builtins/builtins-test/benches/mem.rs index 3f83926b6c5a2..875e4b8699778 100644 --- a/library/compiler-builtins/builtins-test/benches/mem.rs +++ b/library/compiler-builtins/builtins-test/benches/mem.rs @@ -1,72 +1,39 @@ #![feature(test)] extern crate test; +use builtins_test::mem::AlignedSlice; use test::{Bencher, black_box}; extern crate compiler_builtins; use compiler_builtins::mem::{memcmp, memcpy, memmove, memset}; -const WORD_SIZE: usize = core::mem::size_of::(); - -struct AlignedVec { - vec: Vec, - size: usize, -} - -impl AlignedVec { - fn new(fill: u8, size: usize) -> Self { - let mut broadcast = fill as usize; - let mut bits = 8; - while bits < WORD_SIZE * 8 { - broadcast |= broadcast << bits; - bits *= 2; - } - - let vec = vec![broadcast; (size + WORD_SIZE - 1) & !WORD_SIZE]; - AlignedVec { vec, size } - } -} - -impl core::ops::Deref for AlignedVec { - type Target = [u8]; - fn deref(&self) -> &[u8] { - unsafe { core::slice::from_raw_parts(self.vec.as_ptr() as *const u8, self.size) } - } -} - -impl core::ops::DerefMut for AlignedVec { - fn deref_mut(&mut self) -> &mut [u8] { - unsafe { core::slice::from_raw_parts_mut(self.vec.as_mut_ptr() as *mut u8, self.size) } - } -} - fn memcpy_builtin(b: &mut Bencher, n: usize, offset1: usize, offset2: usize) { - let v1 = AlignedVec::new(1, n + offset1); - let mut v2 = AlignedVec::new(0, n + offset2); + let v1 = AlignedSlice::new(1, n, offset1); + let mut v2 = AlignedSlice::new(0, n, offset2); b.bytes = n as u64; b.iter(|| { - let src: &[u8] = black_box(&v1[offset1..]); - let dst: &mut [u8] = black_box(&mut v2[offset2..]); + let src: &[u8] = black_box(&v1); + let dst: &mut [u8] = black_box(&mut v2); dst.copy_from_slice(src); }) } fn memcpy_rust(b: &mut Bencher, n: usize, offset1: usize, offset2: usize) { - let v1 = AlignedVec::new(1, n + offset1); - let mut v2 = AlignedVec::new(0, n + offset2); + let v1 = AlignedSlice::new(1, n, offset1); + let mut v2 = AlignedSlice::new(0, n, offset2); b.bytes = n as u64; b.iter(|| { - let src: &[u8] = black_box(&v1[offset1..]); - let dst: &mut [u8] = black_box(&mut v2[offset2..]); + let src: &[u8] = black_box(&v1); + let dst: &mut [u8] = black_box(&mut v2); unsafe { memcpy(dst.as_mut_ptr(), src.as_ptr(), n) } }) } fn memset_builtin(b: &mut Bencher, n: usize, offset: usize) { - let mut v1 = AlignedVec::new(0, n + offset); + let mut v1 = AlignedSlice::new(0, n, offset); b.bytes = n as u64; b.iter(|| { - let dst: &mut [u8] = black_box(&mut v1[offset..]); + let dst: &mut [u8] = black_box(&mut v1); let val: u8 = black_box(27); for b in dst { *b = val; @@ -75,18 +42,18 @@ fn memset_builtin(b: &mut Bencher, n: usize, offset: usize) { } fn memset_rust(b: &mut Bencher, n: usize, offset: usize) { - let mut v1 = AlignedVec::new(0, n + offset); + let mut v1 = AlignedSlice::new(0, n, offset); b.bytes = n as u64; b.iter(|| { - let dst: &mut [u8] = black_box(&mut v1[offset..]); + let dst: &mut [u8] = black_box(&mut v1); let val = black_box(27); unsafe { memset(dst.as_mut_ptr(), val, n) } }) } fn memcmp_builtin(b: &mut Bencher, n: usize) { - let v1 = AlignedVec::new(0, n); - let mut v2 = AlignedVec::new(0, n); + let v1 = AlignedSlice::new(0, n, 0); + let mut v2 = AlignedSlice::new(0, n, 0); v2[n - 1] = 1; b.bytes = n as u64; b.iter(|| { @@ -97,20 +64,20 @@ fn memcmp_builtin(b: &mut Bencher, n: usize) { } fn memcmp_builtin_unaligned(b: &mut Bencher, n: usize) { - let v1 = AlignedVec::new(0, n); - let mut v2 = AlignedVec::new(0, n); + let v1 = AlignedSlice::new(0, n, 0); + let mut v2 = AlignedSlice::new(0, n, 1); v2[n - 1] = 1; b.bytes = n as u64; b.iter(|| { - let s1: &[u8] = black_box(&v1[0..]); - let s2: &[u8] = black_box(&v2[1..]); + let s1: &[u8] = black_box(&v1); + let s2: &[u8] = black_box(&v2); s1.cmp(s2) }) } fn memcmp_rust(b: &mut Bencher, n: usize) { - let v1 = AlignedVec::new(0, n); - let mut v2 = AlignedVec::new(0, n); + let v1 = AlignedSlice::new(0, n, 0); + let mut v2 = AlignedSlice::new(0, n, 0); v2[n - 1] = 1; b.bytes = n as u64; b.iter(|| { @@ -121,19 +88,20 @@ fn memcmp_rust(b: &mut Bencher, n: usize) { } fn memcmp_rust_unaligned(b: &mut Bencher, n: usize) { - let v1 = AlignedVec::new(0, n); - let mut v2 = AlignedVec::new(0, n); + let v1 = AlignedSlice::new(0, n, 0); + let mut v2 = AlignedSlice::new(0, n, 1); v2[n - 1] = 1; b.bytes = n as u64; b.iter(|| { - let s1: &[u8] = black_box(&v1[0..]); - let s2: &[u8] = black_box(&v2[1..]); - unsafe { memcmp(s1.as_ptr(), s2.as_ptr(), n - 1) } + let s1: &[u8] = black_box(&v1); + let s2: &[u8] = black_box(&v2); + unsafe { memcmp(s1.as_ptr(), s2.as_ptr(), n) } }) } fn memmove_builtin(b: &mut Bencher, n: usize, offset: usize) { - let mut v = AlignedVec::new(0, n + n / 2 + offset); + // Aligned source, misaligned dest + let mut v = AlignedSlice::new(0, n + n / 2 + offset, 0); b.bytes = n as u64; b.iter(|| { let s: &mut [u8] = black_box(&mut v); @@ -142,7 +110,8 @@ fn memmove_builtin(b: &mut Bencher, n: usize, offset: usize) { } fn memmove_rust(b: &mut Bencher, n: usize, offset: usize) { - let mut v = AlignedVec::new(0, n + n / 2 + offset); + // Aligned source, misaligned dest + let mut v = AlignedSlice::new(0, n + n / 2 + offset, 0); b.bytes = n as u64; b.iter(|| { let dst: *mut u8 = black_box(&mut v[n / 2 + offset..]).as_mut_ptr(); diff --git a/library/compiler-builtins/builtins-test/benches/mem_icount.rs b/library/compiler-builtins/builtins-test/benches/mem_icount.rs index 7a3cad09b4044..ff03269dc7142 100644 --- a/library/compiler-builtins/builtins-test/benches/mem_icount.rs +++ b/library/compiler-builtins/builtins-test/benches/mem_icount.rs @@ -2,74 +2,15 @@ //! is stable enough to be tested in CI. use std::hint::black_box; -use std::{ops, slice}; -use compiler_builtins::mem::{memcmp, memcpy, memmove, memset}; +use builtins_test::mem::{AlignedSlice, MEG1}; use gungraun::{library_benchmark, library_benchmark_group, main}; -const PAGE_SIZE: usize = 0x1000; // 4 kiB -const MAX_ALIGN: usize = 512; // assume we may use avx512 operations one day -const MEG1: usize = 1 << 20; // 1 MiB - -#[derive(Clone)] -#[repr(C, align(0x1000))] -struct Page([u8; PAGE_SIZE]); - -/// A buffer that is page-aligned by default, with an optional offset to create a -/// misalignment. -struct AlignedSlice { - buf: Box<[Page]>, - len: usize, - offset: usize, -} - -impl AlignedSlice { - /// Allocate a slice aligned to ALIGN with at least `len` items, with `offset` from - /// page alignment. - fn new_zeroed(len: usize, offset: usize) -> Self { - assert!(offset < PAGE_SIZE); - let total_len = len + offset; - let items = (total_len / PAGE_SIZE) + if total_len % PAGE_SIZE > 0 { 1 } else { 0 }; - let buf = vec![Page([0u8; PAGE_SIZE]); items].into_boxed_slice(); - AlignedSlice { buf, len, offset } - } -} - -impl ops::Deref for AlignedSlice { - type Target = [u8]; - fn deref(&self) -> &Self::Target { - unsafe { slice::from_raw_parts(self.buf.as_ptr().cast::().add(self.offset), self.len) } - } -} - -impl ops::DerefMut for AlignedSlice { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { - slice::from_raw_parts_mut( - self.buf.as_mut_ptr().cast::().add(self.offset), - self.len, - ) - } - } -} - mod mcpy { - use super::*; - - struct Cfg { - len: usize, - s_off: usize, - d_off: usize, - } + use builtins_test::mem::mcpy::{Cfg, setup}; + use compiler_builtins::mem::memcpy; - fn setup(cfg: Cfg) -> (usize, AlignedSlice, AlignedSlice) { - let Cfg { len, s_off, d_off } = cfg; - println!("bytes: {len} bytes, src offset: {s_off}, dst offset: {d_off}"); - let mut src = AlignedSlice::new_zeroed(len, s_off); - let dst = AlignedSlice::new_zeroed(len, d_off); - src.fill(1); - (len, src, dst) - } + use super::*; #[library_benchmark] #[benches::aligned( @@ -85,7 +26,7 @@ mod mcpy { setup = setup, )] #[benches::offset( - // Both at the same offset + // Both unaligned but at the same offset args = [ Cfg { len: 16, s_off: 65, d_off: 65 }, Cfg { len: 32, s_off: 65, d_off: 65 }, @@ -122,17 +63,10 @@ mod mcpy { } mod mset { - use super::*; - - struct Cfg { - len: usize, - offset: usize, - } + use builtins_test::mem::mset::{Cfg, setup}; + use compiler_builtins::mem::memset; - fn setup(Cfg { len, offset }: Cfg) -> (usize, AlignedSlice) { - println!("bytes: {len}, offset: {offset}"); - (len, AlignedSlice::new_zeroed(len, offset)) - } + use super::*; #[library_benchmark] #[benches::aligned( @@ -171,22 +105,10 @@ mod mset { } mod mcmp { - use super::*; - - struct Cfg { - len: usize, - s_off: usize, - d_off: usize, - } + use builtins_test::mem::mcmp::{Cfg, setup}; + use compiler_builtins::mem::memcmp; - fn setup(cfg: Cfg) -> (usize, AlignedSlice, AlignedSlice) { - let Cfg { len, s_off, d_off } = cfg; - println!("bytes: {len}, src offset: {s_off}, dst offset: {d_off}"); - let b1 = AlignedSlice::new_zeroed(len, s_off); - let mut b2 = AlignedSlice::new_zeroed(len, d_off); - b2[len - 1] = 1; - (len, b1, b2) - } + use super::*; #[library_benchmark] #[benches::aligned( @@ -240,68 +162,11 @@ mod mcmp { mod mmove { use Spread::{Aligned, Large, Medium, Small}; + use builtins_test::mem::mmove::{Cfg, Spread, setup_backward, setup_forward}; + use compiler_builtins::mem::memmove; use super::*; - struct Cfg { - len: usize, - spread: Spread, - off: usize, - } - - enum Spread { - /// `src` and `dst` are close and have the same alignment (or offset). - Aligned, - /// `src` and `dst` are close. - Small, - /// `src` and `dst` are halfway offset in the buffer. - Medium, - /// `src` and `dst` only overlap by a single byte. - Large, - } - - // Note that small and large are - fn calculate_spread(len: usize, spread: Spread) -> usize { - match spread { - // Note that this test doesn't make sense for lengths less than len=128 - Aligned => { - assert!(len > MAX_ALIGN, "aligned memset would have no overlap"); - MAX_ALIGN - } - Small => 1, - Medium => (len / 2) + 1, // add 1 so all are misaligned - Large => len - 1, - } - } - - fn setup_forward(cfg: Cfg) -> (usize, usize, AlignedSlice) { - let Cfg { len, spread, off } = cfg; - let spread = calculate_spread(len, spread); - println!("bytes: {len}, spread: {spread}, offset: {off}, forward"); - assert!(spread < len, "memmove tests should have some overlap"); - let mut buf = AlignedSlice::new_zeroed(len + spread, off); - let mut fill: usize = 0; - buf[..len].fill_with(|| { - fill += 1; - fill as u8 - }); - (len, spread, buf) - } - - fn setup_backward(cfg: Cfg) -> (usize, usize, AlignedSlice) { - let Cfg { len, spread, off } = cfg; - let spread = calculate_spread(len, spread); - println!("bytes: {len}, spread: {spread}, offset: {off}, backward"); - assert!(spread < len, "memmove tests should have some overlap"); - let mut buf = AlignedSlice::new_zeroed(len + spread, off); - let mut fill: usize = 0; - buf[spread..].fill_with(|| { - fill += 1; - fill as u8 - }); - (len, spread, buf) - } - #[library_benchmark] #[benches::aligned( args = [ @@ -492,9 +357,50 @@ mod mmove { library_benchmark_group!(name = memmove, benchmarks = [forward_move, backward_move]); } +mod slen { + use builtins_test::mem::slen::{Cfg, setup}; + use compiler_builtins::mem::strlen; + + use super::*; + + #[library_benchmark] + #[benches::aligned( + args = [ + Cfg { len: 1, offset: 0 }, + Cfg { len: 16, offset: 0 }, + Cfg { len: 32, offset: 0 }, + Cfg { len: 64, offset: 0 }, + Cfg { len: 512, offset: 0 }, + Cfg { len: 4096, offset: 0 }, + Cfg { len: MEG1, offset: 0 }, + ], + setup = setup, + )] + #[benches::offset( + args = [ + Cfg { len: 1, offset: 65 }, + Cfg { len: 16, offset: 65 }, + Cfg { len: 32, offset: 65 }, + Cfg { len: 64, offset: 65 }, + Cfg { len: 512, offset: 65 }, + Cfg { len: 4096, offset: 65 }, + Cfg { len: MEG1, offset: 65 }, + ], + setup = setup, + )] + fn bench_strlen(s: AlignedSlice) { + unsafe { + black_box(strlen(black_box(s.as_ptr().cast::()))); + } + } + + library_benchmark_group!(name = strlen, benchmarks = [bench_strlen]); +} + use mcmp::memcmp; use mcpy::memcpy; use mmove::memmove; use mset::memset; +use slen::strlen; -main!(library_benchmark_groups = [memcpy, memset, memcmp, memmove]); +main!(library_benchmark_groups = [memcpy, memset, memcmp, memmove, strlen]); diff --git a/library/compiler-builtins/builtins-test/build.rs b/library/compiler-builtins/builtins-test/build.rs index 133186bc7f57d..b36d581b9d195 100644 --- a/library/compiler-builtins/builtins-test/build.rs +++ b/library/compiler-builtins/builtins-test/build.rs @@ -58,12 +58,6 @@ fn main() { if cfg.target_arch == "arm" || cfg.target_vendor == "apple" || cfg.target_env == "msvc" - // GCC and LLVM disagree on the ABI of `f16` and `f128` with MinGW. See - // . - || (cfg.target_os == "windows" && cfg.target_env == "gnu") - // FIXME(llvm): There is an ABI incompatibility between GCC and Clang on 32-bit x86. - // See . - || cfg.target_arch == "x86" // 32-bit PowerPC and 64-bit LE gets code generated that Qemu cannot handle. See // . || cfg.target_arch == "powerpc" diff --git a/library/compiler-builtins/builtins-test/src/bench.rs b/library/compiler-builtins/builtins-test/src/bench.rs index dd03579285cbc..2985303988287 100644 --- a/library/compiler-builtins/builtins-test/src/bench.rs +++ b/library/compiler-builtins/builtins-test/src/bench.rs @@ -76,11 +76,11 @@ macro_rules! float_bench { sig: ($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty, // Path to the crate in compiler_builtins crate_fn: $crate_fn:path, - // Optional alias on ppc + // Optional name on ppc $( crate_fn_ppc: $crate_fn_ppc:path, )? // Name of the system symbol sys_fn: $sys_fn:ident, - // Optional alias on ppc + // Optional name on ppc $( sys_fn_ppc: $sys_fn_ppc:path, )? // Meta saying whether the system symbol is available sys_available: $sys_available:meta, @@ -122,7 +122,7 @@ macro_rules! float_bench { #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] let target_crate_fn = $crate_fn; - // On PPC, use an alias if specified + // On PPC, use the PPC name if specified #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] let target_crate_fn = float_bench!(@coalesce $($crate_fn_ppc)?, $crate_fn); @@ -135,7 +135,7 @@ macro_rules! float_bench { #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] let target_sys_fn = $sys_fn; - // On PPC, use an alias if specified + // On PPC, use the PPC name if specified #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] let target_sys_fn = float_bench!(@coalesce $($sys_fn_ppc)?, $sys_fn); diff --git a/library/compiler-builtins/builtins-test/src/lib.rs b/library/compiler-builtins/builtins-test/src/lib.rs index 56c04e551df9d..ebd0162dfae05 100644 --- a/library/compiler-builtins/builtins-test/src/lib.rs +++ b/library/compiler-builtins/builtins-test/src/lib.rs @@ -17,6 +17,7 @@ #![cfg_attr(f16_enabled, feature(f16))] pub mod bench; +pub mod mem; extern crate alloc; use compiler_builtins::support::{Float, Int, MinInt}; diff --git a/library/compiler-builtins/builtins-test/src/mem.rs b/library/compiler-builtins/builtins-test/src/mem.rs new file mode 100644 index 0000000000000..aa66890d56180 --- /dev/null +++ b/library/compiler-builtins/builtins-test/src/mem.rs @@ -0,0 +1,212 @@ +extern crate alloc; + +use alloc::boxed::Box; +use alloc::vec; +use core::{ops, slice}; + +/// 4 kiB +pub const PAGE_SIZE: usize = 0x1000; +/// 1 MiB +pub const MEG1: usize = 1 << 20; +/// When we want to test behavior that may depend on aligned reads/writes, use this value. Large +/// enough for AVX512. +pub const MAX_TESTED_ALIGN: usize = 512; + +#[derive(Clone)] +#[repr(C, align(0x1000))] +struct Page([u8; PAGE_SIZE]); + +/// A buffer that is page-aligned by default and dereferences to a slice, with an optional offset +/// for the deref to create a misaligned buffer. +pub struct AlignedSlice { + buf: Box<[Page]>, + len: usize, + offset: usize, +} + +impl AlignedSlice { + /// Allocate a slice aligned to ALIGN with at least `len` items, with `offset` from + /// page alignment. + pub fn new(fill: u8, len: usize, offset: usize) -> Self { + assert!(offset < PAGE_SIZE); + let total_len = len + offset; + let limbs = total_len.div_ceil(PAGE_SIZE); + let buf = vec![Page([fill; PAGE_SIZE]); limbs].into_boxed_slice(); + AlignedSlice { buf, len, offset } + } + + /// Same as [`new`] but with 0 as the value. + pub fn new_zeroed(len: usize, offset: usize) -> Self { + AlignedSlice::new(0, len, offset) + } +} + +impl ops::Deref for AlignedSlice { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + unsafe { slice::from_raw_parts(self.buf.as_ptr().cast::().add(self.offset), self.len) } + } +} + +impl ops::DerefMut for AlignedSlice { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + slice::from_raw_parts_mut( + self.buf.as_mut_ptr().cast::().add(self.offset), + self.len, + ) + } + } +} + +pub mod mcpy { + use super::*; + + pub struct Cfg { + pub len: usize, + pub s_off: usize, + pub d_off: usize, + } + + /// Return `(len, src, dst)` for a cfg. + pub fn setup(cfg: Cfg) -> (usize, AlignedSlice, AlignedSlice) { + let Cfg { len, s_off, d_off } = cfg; + let mut src = AlignedSlice::new_zeroed(len, s_off); + let dst = AlignedSlice::new_zeroed(len, d_off); + src.fill(1); + (len, src, dst) + } +} + +pub mod mset { + use super::*; + + pub struct Cfg { + pub len: usize, + pub offset: usize, + } + + pub fn setup(Cfg { len, offset }: Cfg) -> (usize, AlignedSlice) { + (len, AlignedSlice::new_zeroed(len, offset)) + } +} + +pub mod mcmp { + use super::*; + + pub struct Cfg { + pub len: usize, + pub s_off: usize, + pub d_off: usize, + } + + pub fn setup(cfg: Cfg) -> (usize, AlignedSlice, AlignedSlice) { + let Cfg { len, s_off, d_off } = cfg; + let b1 = AlignedSlice::new_zeroed(len, s_off); + let mut b2 = AlignedSlice::new_zeroed(len, d_off); + b2[len - 1] = 1; + (len, b1, b2) + } +} + +pub mod mmove { + use Spread::{Aligned, Large, Medium, Small}; + + use super::*; + + pub struct Cfg { + pub len: usize, + pub spread: Spread, + pub off: usize, + } + + pub enum Spread { + /// `src` and `dst` are close and have the same alignment (or offset). + Aligned, + /// `src` and `dst` are close. + Small, + /// `src` and `dst` are halfway offset in the buffer. + Medium, + /// `src` and `dst` only overlap by a single byte. + Large, + } + + // Note that small and large are + pub fn calculate_spread(len: usize, spread: Spread) -> usize { + match spread { + // Note that this test doesn't make sense for lengths less than len=128 + Aligned => { + assert!( + len > MAX_TESTED_ALIGN, + "aligned memset would have no overlap" + ); + MAX_TESTED_ALIGN + } + Small => 1, + Medium => (len / 2) + 1, // add 1 so all are misaligned + Large => len - 1, + } + } + + pub fn setup_forward(cfg: Cfg) -> (usize, usize, AlignedSlice) { + let Cfg { len, spread, off } = cfg; + let spread = calculate_spread(len, spread); + assert!(spread < len, "memmove tests should have some overlap"); + let mut buf = AlignedSlice::new_zeroed(len + spread, off); + let mut fill: usize = 0; + buf[..len].fill_with(|| { + fill += 1; + fill as u8 + }); + (len, spread, buf) + } + + pub fn setup_backward(cfg: Cfg) -> (usize, usize, AlignedSlice) { + let Cfg { len, spread, off } = cfg; + let spread = calculate_spread(len, spread); + assert!(spread < len, "memmove tests should have some overlap"); + let mut buf = AlignedSlice::new_zeroed(len + spread, off); + let mut fill: usize = 0; + buf[spread..].fill_with(|| { + fill += 1; + fill as u8 + }); + (len, spread, buf) + } +} + +pub mod slen { + use super::*; + + pub struct Cfg { + pub len: usize, + pub offset: usize, + } + + pub fn setup(Cfg { len, offset }: Cfg) -> AlignedSlice { + assert!(len > 0, "must have one byte for the \\0"); + let mut ret = AlignedSlice::new(b'x', len, offset); + ret[len - 1] = 0; + ret + } +} + +#[test] +fn test_alignment() { + let v = AlignedSlice::new_zeroed(1, 0); + assert_eq!(v.len(), 1); + assert_eq!(v.as_ptr().addr() % PAGE_SIZE, 0); + + let v = AlignedSlice::new_zeroed(PAGE_SIZE + 1, 0); + assert_eq!(v.len(), PAGE_SIZE + 1); + assert_eq!(v.as_ptr().addr() % PAGE_SIZE, 0); + + let v = AlignedSlice::new_zeroed(1, 1); + assert_eq!(v.len(), 1); + assert_eq!(v.as_ptr().addr() % 2, 1); + + let v = AlignedSlice::new_zeroed(1, 64); + assert_eq!(v.len(), 1); + assert_eq!(v.as_ptr().addr() % 64, 0); + assert_eq!(v.as_ptr().addr() % 128, 64); +} diff --git a/library/compiler-builtins/builtins-test/tests/complex.rs b/library/compiler-builtins/builtins-test/tests/complex.rs new file mode 100644 index 0000000000000..a67a1b3c58783 --- /dev/null +++ b/library/compiler-builtins/builtins-test/tests/complex.rs @@ -0,0 +1,426 @@ +#![cfg_attr(f16_enabled, feature(f16))] +#![cfg_attr(f128_enabled, feature(f128))] +#![feature(complex_numbers)] +#![allow(unused_features)] + +mod complex { + use core::num::Complex; + + use compiler_builtins::support::Float; + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + enum Class { + /// Both components are NaN. + NaN, + /// At least one component is infinite. + Infinite, + /// Both components are zero. + Zero, + /// One component is a "regular" number, the other is NaN. + NonZeroAndNaN, + /// Both components are "regular" numbers. + NonZero, + } + + fn classify(c: Complex) -> Class { + if c.re == F::ZERO && c.im == F::ZERO { + Class::Zero + } else if c.re.is_infinite() || c.im.is_infinite() { + Class::Infinite + } else if c.re.is_nan() && c.im.is_nan() { + Class::NaN + } else if c.re.is_nan() { + if c.im == F::ZERO { + Class::NaN + } else { + Class::NonZeroAndNaN + } + } else if c.im.is_nan() { + if c.re == F::ZERO { + Class::NaN + } else { + Class::NonZeroAndNaN + } + } else { + Class::NonZero + } + } + + fn test_mul(p: Complex, q: Complex, actual: Complex, tolerance: F) -> bool { + let expected = match classify(p) { + Class::Zero => match classify(q) { + Class::Zero | Class::NonZero => Class::Zero, + Class::Infinite | Class::NaN | Class::NonZeroAndNaN => Class::NaN, + }, + + Class::NonZero => match classify(q) { + Class::Zero => Class::Zero, + Class::NonZero => { + if classify(actual) != Class::NonZero { + return true; + } + + let Complex { re: a, im: b } = p; + let Complex { re: c, im: d } = q; + + let z = Complex::new(a * c - b * d, a * d + b * c); + let r = actual; + + let diff_re = r.re - z.re; + let diff_im = r.im - z.im; + + let diff_sq = diff_re * diff_re + diff_im * diff_im; + let mag_sq = r.re * r.re + r.im * r.im; + + if diff_sq > (tolerance * tolerance) * mag_sq { + return true; + } + + return false; + } + Class::Infinite => Class::Infinite, + Class::NaN | Class::NonZeroAndNaN => Class::NaN, + }, + + Class::Infinite => match classify(q) { + Class::Zero | Class::NaN => Class::NaN, + Class::NonZero | Class::Infinite | Class::NonZeroAndNaN => Class::Infinite, + }, + + Class::NaN => Class::NaN, + + Class::NonZeroAndNaN => match classify(q) { + Class::Infinite => Class::Infinite, + Class::Zero | Class::NonZero | Class::NaN | Class::NonZeroAndNaN => Class::NaN, + }, + }; + + classify(actual) != expected + } + + fn test_div( + dividend: Complex, + divisor: Complex, + actual: Complex, + tolerance: F, + ) -> bool { + let expected = match classify(dividend) { + Class::Zero => match classify(divisor) { + Class::Zero => Class::NaN, + Class::NonZero => Class::Zero, + Class::Infinite => Class::Zero, + Class::NaN => Class::NaN, + Class::NonZeroAndNaN => Class::NaN, + }, + + Class::NonZero => match classify(divisor) { + Class::Zero => Class::Infinite, + Class::NonZero => { + if classify(actual) != Class::NonZero { + return true; + } + + let Complex { re: a, im: b } = dividend; + let Complex { re: c, im: d } = divisor; + + let denominator = c * c + d * d; + let z = Complex::new( + (a * c + b * d) / denominator, // + (b * c - a * d) / denominator, + ); + + let r = actual; + + let diff_re = r.re - z.re; + let diff_im = r.im - z.im; + + let diff_sq = diff_re * diff_re + diff_im * diff_im; + let mag_sq = r.re * r.re + r.im * r.im; + + if diff_sq > (tolerance * tolerance) * mag_sq { + return true; + } + + return false; + } + Class::Infinite => Class::Zero, + Class::NaN => Class::NaN, + Class::NonZeroAndNaN => Class::NaN, + }, + + Class::Infinite => match classify(divisor) { + Class::Zero | Class::NonZero => Class::Infinite, + Class::Infinite | Class::NaN | Class::NonZeroAndNaN => Class::NaN, + }, + + Class::NaN => Class::NaN, + + Class::NonZeroAndNaN => match classify(divisor) { + Class::Zero => Class::Infinite, + Class::NonZero | Class::Infinite | Class::NaN | Class::NonZeroAndNaN => Class::NaN, + }, + }; + + classify(actual) != expected + } + + macro_rules! complex_test_data { + ($f:ty) => {{ + const INFINITY: $f = <$f>::INFINITY; + const NEG_INFINITY: $f = <$f>::NEG_INFINITY; + const NAN: $f = <$f>::NAN; + const SNAN: $f = <$f>::SNAN; + + #[allow(overflowing_literals)] + let (small, big) = if size_of::<$f>() == 2 { + (1.0e-2, 1.0e2) + } else { + (1.0e-6, 1.0e6) + }; + + [ + Complex::new(small, small), + Complex::new(-small, small), + Complex::new(-small, -small), + Complex::new(small, -small), + Complex::new(big, small), + Complex::new(-big, small), + Complex::new(-big, -small), + Complex::new(big, -small), + Complex::new(small, big), + Complex::new(-small, big), + Complex::new(-small, -big), + Complex::new(small, -big), + Complex::new(big, big), + Complex::new(-big, big), + Complex::new(-big, -big), + Complex::new(big, -big), + Complex::new(NAN, NAN), + Complex::new(NEG_INFINITY, NAN), + Complex::new(-2., NAN), + Complex::new(-1., NAN), + Complex::new(-0.5, NAN), + Complex::new(-0., NAN), + Complex::new(0., NAN), + Complex::new(0.5, NAN), + Complex::new(1., NAN), + Complex::new(2., NAN), + Complex::new(INFINITY, NAN), + Complex::new(NAN, NEG_INFINITY), + Complex::new(NEG_INFINITY, NEG_INFINITY), + Complex::new(-2., NEG_INFINITY), + Complex::new(-1., NEG_INFINITY), + Complex::new(-0.5, NEG_INFINITY), + Complex::new(-0., NEG_INFINITY), + Complex::new(0., NEG_INFINITY), + Complex::new(0.5, NEG_INFINITY), + Complex::new(1., NEG_INFINITY), + Complex::new(2., NEG_INFINITY), + Complex::new(INFINITY, NEG_INFINITY), + Complex::new(NAN, -2.), + Complex::new(NEG_INFINITY, -2.), + Complex::new(-2., -2.), + Complex::new(-1., -2.), + Complex::new(-0.5, -2.), + Complex::new(-0., -2.), + Complex::new(0., -2.), + Complex::new(0.5, -2.), + Complex::new(1., -2.), + Complex::new(2., -2.), + Complex::new(INFINITY, -2.), + Complex::new(NAN, -1.), + Complex::new(NEG_INFINITY, -1.), + Complex::new(-2., -1.), + Complex::new(-1., -1.), + Complex::new(-0.5, -1.), + Complex::new(-0., -1.), + Complex::new(0., -1.), + Complex::new(0.5, -1.), + Complex::new(1., -1.), + Complex::new(2., -1.), + Complex::new(INFINITY, -1.), + Complex::new(NAN, -0.5), + Complex::new(NEG_INFINITY, -0.5), + Complex::new(-2., -0.5), + Complex::new(-1., -0.5), + Complex::new(-0.5, -0.5), + Complex::new(-0., -0.5), + Complex::new(0., -0.5), + Complex::new(0.5, -0.5), + Complex::new(1., -0.5), + Complex::new(2., -0.5), + Complex::new(INFINITY, -0.5), + Complex::new(NAN, -0.), + Complex::new(NEG_INFINITY, -0.), + Complex::new(-2., -0.), + Complex::new(-1., -0.), + Complex::new(-0.5, -0.), + Complex::new(-0., -0.), + Complex::new(0., -0.), + Complex::new(0.5, -0.), + Complex::new(1., -0.), + Complex::new(2., -0.), + Complex::new(INFINITY, -0.), + Complex::new(NAN, 0.), + Complex::new(NEG_INFINITY, 0.), + Complex::new(-2., 0.), + Complex::new(-1., 0.), + Complex::new(-0.5, 0.), + Complex::new(-0., 0.), + Complex::new(0., 0.), + Complex::new(0.5, 0.), + Complex::new(1., 0.), + Complex::new(2., 0.), + Complex::new(INFINITY, 0.), + Complex::new(NAN, 0.5), + Complex::new(NEG_INFINITY, 0.5), + Complex::new(-2., 0.5), + Complex::new(-1., 0.5), + Complex::new(-0.5, 0.5), + Complex::new(-0., 0.5), + Complex::new(0., 0.5), + Complex::new(0.5, 0.5), + Complex::new(1., 0.5), + Complex::new(2., 0.5), + Complex::new(INFINITY, 0.5), + Complex::new(NAN, 1.), + Complex::new(NEG_INFINITY, 1.), + Complex::new(-2., 1.), + Complex::new(-1., 1.), + Complex::new(-0.5, 1.), + Complex::new(-0., 1.), + Complex::new(0., 1.), + Complex::new(0.5, 1.), + Complex::new(1., 1.), + Complex::new(2., 1.), + Complex::new(INFINITY, 1.), + Complex::new(NAN, 2.), + Complex::new(NEG_INFINITY, 2.), + Complex::new(-2., 2.), + Complex::new(-1., 2.), + Complex::new(-0.5, 2.), + Complex::new(-0., 2.), + Complex::new(0., 2.), + Complex::new(0.5, 2.), + Complex::new(1., 2.), + Complex::new(2., 2.), + Complex::new(INFINITY, 2.), + Complex::new(NAN, INFINITY), + Complex::new(NEG_INFINITY, INFINITY), + Complex::new(-2., INFINITY), + Complex::new(-1., INFINITY), + Complex::new(-0.5, INFINITY), + Complex::new(-0., INFINITY), + Complex::new(0., INFINITY), + Complex::new(0.5, INFINITY), + Complex::new(1., INFINITY), + Complex::new(2., INFINITY), + Complex::new(INFINITY, INFINITY), + Complex::new(INFINITY, SNAN), + ] + }}; + } + + macro_rules! complex_mul { + ($($f:ty, $fn:ident, $tolerance:literal);*;) => { + + $( + #[test] + fn $fn() { + use compiler_builtins::float::complex::mul::$fn; + + let input = complex_test_data!($f); + + for p in input { + for q in input { + let Complex{ re: a, im: b } = p; + let Complex{ re: c, im: d } = q; + + let actual = $fn(a, b, c, d); + + assert!( + !test_mul(p, q, actual, $tolerance), + "{func}({a:?}, {b:?}, {c:?}, {d:?}): incorrect ({:?}, {:?})", + actual.re, + actual.im, + func = stringify!($fn), + ); + } + } + } + )* + }; + } + + macro_rules! complex_div { + ($($f:ty, $fn:ident, $tolerance:literal);*;) => { + $( + #[test] + fn $fn() { + use compiler_builtins::float::complex::div::$fn; + + let input = complex_test_data!($f); + + for p in input { + for q in input { + let Complex{ re: a, im: b } = p; + let Complex{ re: c, im: d } = q; + + let actual = $fn(a, b, c, d); + + assert!( + !test_div(p, q, actual, $tolerance), + "{func}({a:?}, {b:?}, {c:?}, {d:?}): incorrect ({:?}, {:?})", + actual.re, + actual.im, + func = stringify!($fn), + ); + } + } + } + )* + }; + } + + #[cfg(all(f16_enabled, not(x86_no_sse2)))] + complex_mul! { + f16, __rust_mulhc3, 1.0e-3; + } + + #[cfg(all(f16_enabled, not(x86_no_sse2)))] + complex_div! { + f16, __rust_divhc3, 1.0e-3; + } + + complex_mul! { + f32, __rust_mulsc3, 1.0e-6; + f64, __rust_muldc3, 1.0e-9; + } + + complex_div! { + f32, __rust_divsc3, 1.0e-6; + f64, __rust_divdc3, 1.0e-9; + } + + #[cfg(f128_enabled)] + cfg_select! { + any(target_arch = "powerpc", target_arch = "powerpc64") => { + complex_mul! { + f128, __rust_mulkc3, 1.0e-12; + } + + complex_div! { + f128, __rust_divkc3, 1.0e-12; + } + } + _ => { + complex_mul! { + f128, __rust_multc3, 1.0e-12; + } + + complex_div! { + f128, __rust_divtc3, 1.0e-12; + } + } + } +} diff --git a/library/compiler-builtins/builtins-test/tests/mem.rs b/library/compiler-builtins/builtins-test/tests/mem.rs index d838ef159a024..a10dddbd19c1f 100644 --- a/library/compiler-builtins/builtins-test/tests/mem.rs +++ b/library/compiler-builtins/builtins-test/tests/mem.rs @@ -1,5 +1,4 @@ -extern crate compiler_builtins; -use compiler_builtins::mem::{memcmp, memcpy, memmove, memset}; +use compiler_builtins::mem::{memcmp, memcpy, memmove, memset, strlen}; const WORD_SIZE: usize = core::mem::size_of::(); @@ -284,3 +283,13 @@ fn memset_backward_aligned() { assert_eq!(arr.0, reference.0); } } + +#[test] +fn test_strlen() { + unsafe { + let s = c""; + assert_eq!(strlen(s.as_ptr()), 0); + let s = c"hello, world!"; + assert_eq!(strlen(s.as_ptr()), 13); + } +} diff --git a/library/compiler-builtins/ci/compiler-rt-patches/0001-compiler-rt-Disable-executable-stack-on-aeabi_u-read.patch b/library/compiler-builtins/ci/compiler-rt-patches/0001-compiler-rt-Disable-executable-stack-on-aeabi_u-read.patch deleted file mode 100644 index 13f4bc31d08f7..0000000000000 --- a/library/compiler-builtins/ci/compiler-rt-patches/0001-compiler-rt-Disable-executable-stack-on-aeabi_u-read.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 849c51e082b0958524246a0a880f46d468a55147 Mon Sep 17 00:00:00 2001 -From: Trevor Gross -Date: Thu, 6 Aug 2026 09:08:27 -0400 -Subject: [PATCH] [compiler-rt] Disable executable stack on - `aeabi_u{read,write}*.S` (#214465) - -These were missing `NO_EXEC_STACK_DIRECTIVE` to add `.note.GNU-stack`; -without it, a binary including any of these files will have the stack -marked executable. Add the directive here, matching other similar files. - -Symtab diff before: - -$ clang compiler-rt/lib/builtins/arm/aeabi_uread4.S ---target=arm-unknown-linux-gnueabi -c - $ llvm-readelf aeabi_uread4.o -S - There are 5 section headers, starting at offset 0xe4: - - Section Headers: -[Nr] Name Type Address Off Size ES Flg Lk Inf Al -[ 0] NULL 00000000 000000 000000 00 0 0 0 -[ 1] .strtab STRTAB 00000000 0000a8 000039 00 0 0 1 -[ 2] .text PROGBITS 00000000 000034 000020 00 AX 0 0 4 -[ 3] .ARM.attributes ARM_ATTRIBUTES 00000000 000054 000022 00 0 0 1 -[ 4] .symtab SYMTAB 00000000 000078 000030 10 1 2 4 - -After: - -$ clang compiler-rt/lib/builtins/arm/aeabi_uread4.S ---target=arm-unknown-linux-gnueabi -c - $ llvm-readelf aeabi_uread4.o -S - There are 6 section headers, starting at offset 0xf4: - - Section Headers: -[Nr] Name Type Address Off Size ES Flg Lk Inf Al -[ 0] NULL 00000000 000000 000000 00 0 0 0 -[ 1] .strtab STRTAB 00000000 0000a8 000049 00 0 0 1 -[ 2] .text PROGBITS 00000000 000034 000020 00 AX 0 0 4 -[ 3] .note.GNU-stack PROGBITS 00000000 000054 000000 00 0 0 1 -[ 4] .ARM.attributes ARM_ATTRIBUTES 00000000 000054 000022 00 0 0 1 -[ 5] .symtab SYMTAB 00000000 000078 000030 10 1 2 4 - -Fixes: 39413af931a7 ("[Compiler-rt] Implement AEABI Unaligned Read/Write - Helpers in compiler-rt (#167913)") ---- - -Add this patch to avoid a symcheck failure until the LLVM update can work -through. - - compiler-rt/lib/builtins/arm/aeabi_uread4.S | 1 + - compiler-rt/lib/builtins/arm/aeabi_uread8.S | 2 ++ - compiler-rt/lib/builtins/arm/aeabi_uwrite4.S | 2 ++ - compiler-rt/lib/builtins/arm/aeabi_uwrite8.S | 2 ++ - 4 files changed, 7 insertions(+) - -diff --git a/compiler-rt/lib/builtins/arm/aeabi_uread4.S b/compiler-rt/lib/builtins/arm/aeabi_uread4.S -index 4a54890fdf83..05e476a17905 100644 ---- a/compiler-rt/lib/builtins/arm/aeabi_uread4.S -+++ b/compiler-rt/lib/builtins/arm/aeabi_uread4.S -@@ -61,3 +61,4 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_uread4) - #endif - END_COMPILERRT_FUNCTION(__aeabi_uread4) - -+NO_EXEC_STACK_DIRECTIVE -diff --git a/compiler-rt/lib/builtins/arm/aeabi_uread8.S b/compiler-rt/lib/builtins/arm/aeabi_uread8.S -index 32844b8b3c7e..0b12c48ae46f 100644 ---- a/compiler-rt/lib/builtins/arm/aeabi_uread8.S -+++ b/compiler-rt/lib/builtins/arm/aeabi_uread8.S -@@ -98,3 +98,5 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_uread8) - #endif - - END_COMPILERRT_FUNCTION(__aeabi_uread8) -+ -+NO_EXEC_STACK_DIRECTIVE -diff --git a/compiler-rt/lib/builtins/arm/aeabi_uwrite4.S b/compiler-rt/lib/builtins/arm/aeabi_uwrite4.S -index 9f749695910b..7e9a0337b781 100644 ---- a/compiler-rt/lib/builtins/arm/aeabi_uwrite4.S -+++ b/compiler-rt/lib/builtins/arm/aeabi_uwrite4.S -@@ -33,3 +33,5 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_uwrite4) - #endif - bx lr - END_COMPILERRT_FUNCTION(__aeabi_uwrite4) -+ -+NO_EXEC_STACK_DIRECTIVE -diff --git a/compiler-rt/lib/builtins/arm/aeabi_uwrite8.S b/compiler-rt/lib/builtins/arm/aeabi_uwrite8.S -index 8188032fc3bd..763f42ff7ed0 100644 ---- a/compiler-rt/lib/builtins/arm/aeabi_uwrite8.S -+++ b/compiler-rt/lib/builtins/arm/aeabi_uwrite8.S -@@ -49,3 +49,5 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_uwrite8) - #endif - bx lr - END_COMPILERRT_FUNCTION(__aeabi_uwrite8) -+ -+NO_EXEC_STACK_DIRECTIVE --- -2.50.1 (Apple Git-155) diff --git a/library/compiler-builtins/ci/docker/aarch64-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/aarch64-unknown-linux-gnu/Dockerfile index 30a13fc5de910..555191eedecb6 100644 --- a/library/compiler-builtins/ci/docker/aarch64-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/aarch64-unknown-linux-gnu/Dockerfile @@ -1,10 +1,14 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates \ - gcc-aarch64-linux-gnu m4 make libc6-dev-arm64-cross \ +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ + gcc-aarch64-linux-gnu \ + libc6-dev \ + libc6-dev-arm64-cross \ + m4 \ + make \ qemu-user ENV TOOLCHAIN_PREFIX=aarch64-linux-gnu- @@ -12,5 +16,4 @@ ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER=qemu-aarch64 \ AR_aarch64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_aarch64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/aarch64-linux-gnu \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/aarch64-linux-gnu diff --git a/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabi/Dockerfile b/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabi/Dockerfile index 41ff36a49e3bb..a23e3526855f9 100644 --- a/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabi/Dockerfile +++ b/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabi/Dockerfile @@ -1,15 +1,17 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates \ - gcc-arm-linux-gnueabi libc6-dev-armel-cross qemu-user +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ + gcc-arm-linux-gnueabi \ + libc6-dev \ + libc6-dev-armel-cross \ + qemu-user ENV TOOLCHAIN_PREFIX=arm-linux-gnueabi- ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_RUNNER=qemu-arm \ AR_arm_unknown_linux_gnueabi="$TOOLCHAIN_PREFIX"ar \ CC_arm_unknown_linux_gnueabi="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/arm-linux-gnueabi \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/arm-linux-gnueabi diff --git a/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabihf/Dockerfile b/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabihf/Dockerfile index 1fad72c470f03..003cc64c8ddc6 100644 --- a/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabihf/Dockerfile +++ b/library/compiler-builtins/ci/docker/arm-unknown-linux-gnueabihf/Dockerfile @@ -1,15 +1,17 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates \ - gcc-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ + gcc-arm-linux-gnueabihf \ + libc6-dev \ + libc6-dev-armhf-cross \ + qemu-user ENV TOOLCHAIN_PREFIX=arm-linux-gnueabihf- ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_RUNNER=qemu-arm \ AR_arm_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"ar \ CC_arm_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf diff --git a/library/compiler-builtins/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile b/library/compiler-builtins/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile index 039ccd5745256..391096e01c8a6 100644 --- a/library/compiler-builtins/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile +++ b/library/compiler-builtins/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile @@ -1,15 +1,17 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates \ - gcc-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ + gcc-arm-linux-gnueabihf \ + libc6-dev \ + libc6-dev-armhf-cross \ + qemu-user ENV TOOLCHAIN_PREFIX=arm-linux-gnueabihf- ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER=qemu-arm \ AR_armv7_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"ar \ CC_armv7_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf diff --git a/library/compiler-builtins/ci/docker/i586-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/i586-unknown-linux-gnu/Dockerfile index 9319e73dd03f0..8c0aea18a66bd 100644 --- a/library/compiler-builtins/ci/docker/i586-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/i586-unknown-linux-gnu/Dockerfile @@ -1,6 +1,9 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc-multilib m4 make libc6-dev ca-certificates +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc-multilib \ + libc6-dev \ + m4 \ + make diff --git a/library/compiler-builtins/ci/docker/i686-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/i686-unknown-linux-gnu/Dockerfile index 9319e73dd03f0..8c0aea18a66bd 100644 --- a/library/compiler-builtins/ci/docker/i686-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/i686-unknown-linux-gnu/Dockerfile @@ -1,6 +1,9 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc-multilib m4 make libc6-dev ca-certificates +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc-multilib \ + libc6-dev \ + m4 \ + make diff --git a/library/compiler-builtins/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile index 442a13164880c..0684b7cc4bb63 100644 --- a/library/compiler-builtins/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile @@ -1,14 +1,16 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev qemu-user ca-certificates \ - gcc-14-loongarch64-linux-gnu libc6-dev-loong64-cross +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ + gcc-14-loongarch64-linux-gnu \ + libc6-dev \ + libc6-dev-loong64-cross \ + qemu-user ENV CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER=loongarch64-linux-gnu-gcc-14 \ CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER=qemu-loongarch64 \ AR_loongarch64_unknown_linux_gnu=loongarch64-linux-gnu-ar \ CC_loongarch64_unknown_linux_gnu=loongarch64-linux-gnu-gcc-14 \ - QEMU_LD_PREFIX=/usr/loongarch64-linux-gnu \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/loongarch64-linux-gnu diff --git a/library/compiler-builtins/ci/docker/mips-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/mips-unknown-linux-gnu/Dockerfile index 9941a8c2736c0..690d878a23ef1 100644 --- a/library/compiler-builtins/ci/docker/mips-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/mips-unknown-linux-gnu/Dockerfile @@ -1,16 +1,19 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates \ - gcc-mips-linux-gnu libc6-dev-mips-cross \ - binfmt-support qemu-user qemu-system-mips +RUN apt-get update && apt-get install -y --no-install-recommends \ + binfmt-support \ + ca-certificates \ + gcc \ + gcc-mips-linux-gnu \ + libc6-dev \ + libc6-dev-mips-cross \ + qemu-system-mips \ + qemu-user ENV TOOLCHAIN_PREFIX=mips-linux-gnu- ENV CARGO_TARGET_MIPS_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_MIPS_UNKNOWN_LINUX_GNU_RUNNER=qemu-mips \ AR_mips_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_mips_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/mips-linux-gnu \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/mips-linux-gnu diff --git a/library/compiler-builtins/ci/docker/mips64-unknown-linux-gnuabi64/Dockerfile b/library/compiler-builtins/ci/docker/mips64-unknown-linux-gnuabi64/Dockerfile index c20d0a77b81c3..6ff8effb8570d 100644 --- a/library/compiler-builtins/ci/docker/mips64-unknown-linux-gnuabi64/Dockerfile +++ b/library/compiler-builtins/ci/docker/mips64-unknown-linux-gnuabi64/Dockerfile @@ -1,20 +1,18 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ +RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ gcc \ gcc-mips64-linux-gnuabi64 \ libc6-dev \ libc6-dev-mips64-cross \ - qemu-user \ qemu-system-mips + qemu-user \ ENV TOOLCHAIN_PREFIX=mips64-linux-gnuabi64- ENV CARGO_TARGET_MIPS64_UNKNOWN_LINUX_GNUABI64_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_MIPS64_UNKNOWN_LINUX_GNUABI64_RUNNER=qemu-mips64 \ AR_mips64_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"ar \ CC_mips64_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/mips64-linux-gnuabi64 \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/mips64-linux-gnuabi64 diff --git a/library/compiler-builtins/ci/docker/mips64el-unknown-linux-gnuabi64/Dockerfile b/library/compiler-builtins/ci/docker/mips64el-unknown-linux-gnuabi64/Dockerfile index 584f7ffff45a5..445fec6786d32 100644 --- a/library/compiler-builtins/ci/docker/mips64el-unknown-linux-gnuabi64/Dockerfile +++ b/library/compiler-builtins/ci/docker/mips64el-unknown-linux-gnuabi64/Dockerfile @@ -1,8 +1,7 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ +RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ gcc \ gcc-mips64el-linux-gnuabi64 \ @@ -15,5 +14,4 @@ ENV CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_GNUABI64_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_GNUABI64_RUNNER=qemu-mips64el \ AR_mips64el_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"ar \ CC_mips64el_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/mips64el-linux-gnuabi64 \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/mips64el-linux-gnuabi64 diff --git a/library/compiler-builtins/ci/docker/mipsel-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/mipsel-unknown-linux-gnu/Dockerfile index ead99bb9c1132..6d4dc124443b8 100644 --- a/library/compiler-builtins/ci/docker/mipsel-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/mipsel-unknown-linux-gnu/Dockerfile @@ -1,16 +1,18 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates \ - gcc-mipsel-linux-gnu libc6-dev-mipsel-cross \ - binfmt-support qemu-user +RUN apt-get update && apt-get install -y --no-install-recommends \ + binfmt-support \ + ca-certificates \ + gcc \ + gcc-mipsel-linux-gnu \ + libc6-dev \ + libc6-dev-mipsel-cross \ + qemu-user ENV TOOLCHAIN_PREFIX=mipsel-linux-gnu- ENV CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_GNU_RUNNER=qemu-mipsel \ AR_mipsel_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_mipsel_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/mipsel-linux-gnu \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/mipsel-linux-gnu diff --git a/library/compiler-builtins/ci/docker/powerpc-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/powerpc-unknown-linux-gnu/Dockerfile index 74071874ed7cf..025ba1a7c419a 100644 --- a/library/compiler-builtins/ci/docker/powerpc-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/powerpc-unknown-linux-gnu/Dockerfile @@ -1,16 +1,18 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev qemu-user ca-certificates \ - gcc-powerpc-linux-gnu libc6-dev-powerpc-cross \ - qemu-system-ppc +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ + gcc-powerpc-linux-gnu \ + libc6-dev \ + libc6-dev-powerpc-cross \ + qemu-system-ppc \ + qemu-user ENV TOOLCHAIN_PREFIX=powerpc-linux-gnu- ENV CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc \ AR_powerpc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_powerpc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/powerpc-linux-gnu \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/powerpc-linux-gnu diff --git a/library/compiler-builtins/ci/docker/powerpc64-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/powerpc64-unknown-linux-gnu/Dockerfile index ba4fec7160b64..fc6e011aaae58 100644 --- a/library/compiler-builtins/ci/docker/powerpc64-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/powerpc64-unknown-linux-gnu/Dockerfile @@ -1,16 +1,19 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates \ - gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross \ - binfmt-support qemu-user qemu-system-ppc +RUN apt-get update && apt-get install -y --no-install-recommends \ + binfmt-support \ + ca-certificates \ + gcc \ + gcc-powerpc64-linux-gnu \ + libc6-dev \ + libc6-dev-ppc64-cross \ + qemu-system-ppc \ + qemu-user ENV TOOLCHAIN_PREFIX=powerpc64-linux-gnu- ENV CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc64 \ AR_powerpc64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_powerpc64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/powerpc64-linux-gnu \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/powerpc64-linux-gnu diff --git a/library/compiler-builtins/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile index e90d4c8812042..0913b2a609349 100644 --- a/library/compiler-builtins/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile @@ -1,16 +1,18 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev qemu-user ca-certificates \ - gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross \ - qemu-system-ppc +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ + gcc-powerpc64le-linux-gnu \ + libc6-dev \ + libc6-dev-ppc64el-cross \ + qemu-system-ppc \ + qemu-user ENV TOOLCHAIN_PREFIX=powerpc64le-linux-gnu- ENV CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc64le \ AR_powerpc64le_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_powerpc64le_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/powerpc64le-linux-gnu \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/powerpc64le-linux-gnu diff --git a/library/compiler-builtins/ci/docker/riscv64gc-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/riscv64gc-unknown-linux-gnu/Dockerfile index 96442121bcd9e..af8e0f3d4733a 100644 --- a/library/compiler-builtins/ci/docker/riscv64gc-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/riscv64gc-unknown-linux-gnu/Dockerfile @@ -1,16 +1,18 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev qemu-user ca-certificates \ - gcc-riscv64-linux-gnu libc6-dev-riscv64-cross \ - qemu-system-riscv +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ + gcc-riscv64-linux-gnu \ + libc6-dev \ + libc6-dev-riscv64-cross \ + qemu-system-riscv \ + qemu-user ENV TOOLCHAIN_PREFIX=riscv64-linux-gnu- ENV CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_RUNNER=qemu-riscv64 \ AR_riscv64gc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ CC_riscv64gc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ - QEMU_LD_PREFIX=/usr/riscv64-linux-gnu \ - RUST_TEST_THREADS=1 + QEMU_LD_PREFIX=/usr/riscv64-linux-gnu diff --git a/library/compiler-builtins/ci/docker/thumbv6m-none-eabi/Dockerfile b/library/compiler-builtins/ci/docker/thumbv6m-none-eabi/Dockerfile index 463cce94e5540..d77fb4fc60db5 100644 --- a/library/compiler-builtins/ci/docker/thumbv6m-none-eabi/Dockerfile +++ b/library/compiler-builtins/ci/docker/thumbv6m-none-eabi/Dockerfile @@ -1,9 +1,10 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates \ +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ gcc-arm-none-eabi \ + libc6-dev \ libnewlib-arm-none-eabi ENV BUILD_ONLY=1 diff --git a/library/compiler-builtins/ci/docker/thumbv7em-none-eabi/Dockerfile b/library/compiler-builtins/ci/docker/thumbv7em-none-eabi/Dockerfile index 463cce94e5540..d77fb4fc60db5 100644 --- a/library/compiler-builtins/ci/docker/thumbv7em-none-eabi/Dockerfile +++ b/library/compiler-builtins/ci/docker/thumbv7em-none-eabi/Dockerfile @@ -1,9 +1,10 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates \ +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ gcc-arm-none-eabi \ + libc6-dev \ libnewlib-arm-none-eabi ENV BUILD_ONLY=1 diff --git a/library/compiler-builtins/ci/docker/thumbv7em-none-eabihf/Dockerfile b/library/compiler-builtins/ci/docker/thumbv7em-none-eabihf/Dockerfile index 463cce94e5540..d77fb4fc60db5 100644 --- a/library/compiler-builtins/ci/docker/thumbv7em-none-eabihf/Dockerfile +++ b/library/compiler-builtins/ci/docker/thumbv7em-none-eabihf/Dockerfile @@ -1,9 +1,10 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates \ +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ gcc-arm-none-eabi \ + libc6-dev \ libnewlib-arm-none-eabi ENV BUILD_ONLY=1 diff --git a/library/compiler-builtins/ci/docker/thumbv7m-none-eabi/Dockerfile b/library/compiler-builtins/ci/docker/thumbv7m-none-eabi/Dockerfile index 463cce94e5540..d77fb4fc60db5 100644 --- a/library/compiler-builtins/ci/docker/thumbv7m-none-eabi/Dockerfile +++ b/library/compiler-builtins/ci/docker/thumbv7m-none-eabi/Dockerfile @@ -1,9 +1,10 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates \ +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ gcc-arm-none-eabi \ + libc6-dev \ libnewlib-arm-none-eabi ENV BUILD_ONLY=1 diff --git a/library/compiler-builtins/ci/docker/wasm32-unknown-unknown/Dockerfile b/library/compiler-builtins/ci/docker/wasm32-unknown-unknown/Dockerfile index 09f35c3b128d0..0e203d20700ac 100644 --- a/library/compiler-builtins/ci/docker/wasm32-unknown-unknown/Dockerfile +++ b/library/compiler-builtins/ci/docker/wasm32-unknown-unknown/Dockerfile @@ -1,8 +1,10 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc clang libc6-dev ca-certificates +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + clang \ + gcc \ + libc6-dev ENV CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=true diff --git a/library/compiler-builtins/ci/docker/x86_64-unknown-linux-gnu/Dockerfile b/library/compiler-builtins/ci/docker/x86_64-unknown-linux-gnu/Dockerfile index 103c395ee8496..1cf59a802511a 100644 --- a/library/compiler-builtins/ci/docker/x86_64-unknown-linux-gnu/Dockerfile +++ b/library/compiler-builtins/ci/docker/x86_64-unknown-linux-gnu/Dockerfile @@ -1,6 +1,9 @@ ARG IMAGE=ubuntu:26.04 FROM $IMAGE -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - gcc m4 make libc6-dev ca-certificates +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gcc \ + libc6-dev \ + m4 \ + make diff --git a/library/compiler-builtins/ci/download-compiler-rt.sh b/library/compiler-builtins/ci/download-compiler-rt.sh index 55498c61d54b4..414e75c0dd6b7 100755 --- a/library/compiler-builtins/ci/download-compiler-rt.sh +++ b/library/compiler-builtins/ci/download-compiler-rt.sh @@ -8,9 +8,3 @@ rust_llvm_version=23.1-2026-07-22 curl -L --retry 3 -o code.tar.gz "https://github.com/rust-lang/llvm-project/archive/rustc/${rust_llvm_version}.tar.gz" tar xzf code.tar.gz --strip-components 1 llvm-project-rustc-${rust_llvm_version}/compiler-rt - -cd compiler-rt - -for p in ../ci/compiler-rt-patches/*; do - cat "$p" | patch -p 2 -done diff --git a/library/compiler-builtins/ci/install-test-deps.sh b/library/compiler-builtins/ci/install-test-deps.sh new file mode 100755 index 0000000000000..7321e4c5d5440 --- /dev/null +++ b/library/compiler-builtins/ci/install-test-deps.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +set -eux + +target="${1}" + +# Allow setting a channel to account for required components (MinGW) +channel="${2:-nightly}" + +# Some runners (native ppc and s390x, self-hosted) don't have all the dependencies +# we need, so we need to install them. + +needed_deps=() +to_install=() + +if [ "$RUN_IN_DOCKER" != "0" ]; then + needed_deps+=(rustup m4) +fi + +for dep in "${needed_deps[@]}"; do + ! command -v "$dep" && to_install+=("$dep") +done + +if [ ${#to_install[@]} -ne 0 ]; then + if command -v apt-get; then + sudo apt-get update + sudo apt-get install -y "${to_install[@]}" + elif command -v apk; then + doas apk add "${to_install[@]}" + else + echo "No package manager found" + fi +fi + +# Install the correct Rust version +rustup update "$channel" --no-self-update +rustup default "$channel" +rustup target add "$target" diff --git a/library/compiler-builtins/ci/run-docker.sh b/library/compiler-builtins/ci/run-docker.sh index 08f20b934acd5..5bf81bce13516 100755 --- a/library/compiler-builtins/ci/run-docker.sh +++ b/library/compiler-builtins/ci/run-docker.sh @@ -58,7 +58,7 @@ run() { "IMAGE=${DOCKER_BASE_IMAGE:-rustlang/rust:nightly}" ) run_args=(-v "compiler-builtins-cache:/builtins-target") - run_cmd="$run_cmd HOME=/tmp" "USING_CONTAINER_RUSTC=1" + run_cmd="$run_cmd HOME=/tmp USING_CONTAINER_RUSTC=1" fi if [ -d compiler-rt ]; then diff --git a/library/compiler-builtins/ci/run.sh b/library/compiler-builtins/ci/run.sh index 359bf3d945b9b..22c136e2df39f 100755 --- a/library/compiler-builtins/ci/run.sh +++ b/library/compiler-builtins/ci/run.sh @@ -26,6 +26,13 @@ if [ "${USING_CONTAINER_RUSTC:-}" = 1 ]; then rustup target add "$target" fi +# Run the command with its output in a collapsable section +asgroup() { + echo "::group::$*" + "$@" + echo "::endgroup" +} + # If nextest is available, use that command -v cargo-nextest && nextest=1 || nextest=0 if [ "$nextest" = "1" ]; then @@ -57,14 +64,13 @@ else --target "$target" ) - "${test_builtins[@]}" - "${test_builtins[@]}" --release - "${test_builtins[@]}" --features c - "${test_builtins[@]}" --features c --release - "${test_builtins[@]}" --benches - "${test_builtins[@]}" --benches --release - "${test_builtins[@]}" --no-default-features - "${test_builtins[@]}" --no-default-features --release + asgroup "${test_builtins[@]}" + asgroup "${test_builtins[@]}" --release + asgroup "${test_builtins[@]}" --features c + asgroup "${test_builtins[@]}" --features c --release + asgroup "${test_builtins[@]}" --benches --release + asgroup "${test_builtins[@]}" --no-default-features + asgroup "${test_builtins[@]}" --no-default-features --release # Validate that having a verbatim path for the target directory works # (trivial to regress using `/` in paths to build artifacts rather than @@ -75,6 +81,9 @@ else fi fi + +echo "::group::Run symcheck" + # Ensure there are no duplicate symbols or references to `core` when # `compiler-builtins` is built with various features. Symcheck invokes Cargo to # build with the arguments we provide it, then validates the built artifacts. @@ -94,6 +103,11 @@ symcheck_cb_args=(-- --package compiler_builtins --features compiler-builtins) "${symcheck[@]}" "${symcheck_cb_args[@]}" --no-default-features "${symcheck[@]}" "${symcheck_cb_args[@]}" --no-default-features --release +echo "::endgroup" + + +echo "::group::Run intrinsics tests" + run_intrinsics_test() { build_args=(--verbose --manifest-path builtins-test-intrinsics/Cargo.toml) build_args+=("$@") @@ -119,6 +133,8 @@ run_intrinsics_test --features c --release CARGO_PROFILE_DEV_LTO=true run_intrinsics_test CARGO_PROFILE_RELEASE_LTO=true run_intrinsics_test --release +echo "::endgroup" + # Test libm # Make sure a simple build works @@ -190,32 +206,31 @@ else cmd=("${test_runner[@]}" "${mflags[@]}") # Test once without intrinsics - "${cmd[@]}" + asgroup "${cmd[@]}" # Run doctests if they were excluded by nextest - [ "$nextest" = "1" ] && cargo test --doc --exclude compiler_builtins "${mflags[@]}" + [ "$nextest" = "1" ] && asgroup cargo test --doc --exclude compiler_builtins "${mflags[@]}" # Exclude the macros and utile crates from the rest of the tests to save CI # runtime, they shouldn't have anything feature- or opt-level-dependent. cmd+=(--exclude util --exclude libm-macros) # Test once with intrinsics enabled - "${cmd[@]}" --features arch,unstable-intrinsics - "${cmd[@]}" --features arch,unstable-intrinsics --benches + asgroup "${cmd[@]}" --features arch,unstable-intrinsics # Test the same in release mode, which also increases coverage. Also ensure # the soft float routines are checked. - "${cmd[@]}" "$profile_flag" release-checked - "${cmd[@]}" "$profile_flag" release-checked --features arch - "${cmd[@]}" "$profile_flag" release-checked --features arch,unstable-intrinsics - "${cmd[@]}" "$profile_flag" release-checked --features arch,unstable-intrinsics --benches + asgroup "${cmd[@]}" "$profile_flag" release-checked + asgroup "${cmd[@]}" "$profile_flag" release-checked --features arch + asgroup "${cmd[@]}" "$profile_flag" release-checked --features arch,unstable-intrinsics + asgroup "${cmd[@]}" "$profile_flag" release-checked --features arch,unstable-intrinsics --benches # Ensure that the routines do not panic. # # `--tests` must be passed because no-panic is only enabled as a dev # dependency. The `release-opt` profile must be used to enable LTO and a # single CGU. - ENSURE_NO_PANIC=1 cargo build \ + ENSURE_NO_PANIC=1 asgroup cargo build \ -p libm \ --target "$target" \ --no-default-features \ diff --git a/library/compiler-builtins/compiler-builtins/README.md b/library/compiler-builtins/compiler-builtins/README.md index 63cbf33d2aea2..72faec3dc5c97 100644 --- a/library/compiler-builtins/compiler-builtins/README.md +++ b/library/compiler-builtins/compiler-builtins/README.md @@ -174,6 +174,15 @@ of being added to Rust. - [x] trunctfhf2.c - [x] trunctfsf2.c +These builtins involve complex floating-point types that are in the process of +being added to Rust. + +- [x] divdc3.c +- [x] divsc3.c +- [x] divtc3.c +- [x] muldc3.c +- [x] mulsc3.c +- [x] multc3.c These builtins are used by the Hexagon DSP @@ -223,6 +232,12 @@ by Rust. - ~~i386/floatundixf.S~~ - ~~x86_64/floatdixf.c~~ - ~~x86_64/floatundixf.S~~ +- ~~powixf2.c~~ + +These builtins are for complex X87 `f80` floating-point numbers. + +- ~~divxc3.c~~ +- ~~mulxc3.c~~ These builtins are for IBM "extended double" non-IEEE 128-bit floating-point numbers. @@ -248,19 +263,6 @@ supported by Rust. - ~~truncsfbf2.c~~ - ~~trunctfxf2.c~~ -These builtins involve complex floating-point types that are not supported by -Rust. - -- ~~divdc3.c~~ -- ~~divsc3.c~~ -- ~~divtc3.c~~ -- ~~divxc3.c~~ -- ~~muldc3.c~~ -- ~~mulsc3.c~~ -- ~~multc3.c~~ -- ~~mulxc3.c~~ -- ~~powixf2.c~~ - These builtins are never called by LLVM. - ~~absvdi2.c~~ diff --git a/library/compiler-builtins/compiler-builtins/build.rs b/library/compiler-builtins/compiler-builtins/build.rs index 8869add9f5ee4..64a4e3b6c9e9c 100644 --- a/library/compiler-builtins/compiler-builtins/build.rs +++ b/library/compiler-builtins/compiler-builtins/build.rs @@ -297,14 +297,7 @@ mod c { ]); if consider_float_intrinsics { - sources.extend(&[ - ("__divdc3", "divdc3.c"), - ("__divsc3", "divsc3.c"), - ("__muldc3", "muldc3.c"), - ("__mulsc3", "mulsc3.c"), - ("__negdf2", "negdf2.c"), - ("__negsf2", "negsf2.c"), - ]); + sources.extend(&[("__negdf2", "negdf2.c"), ("__negsf2", "negsf2.c")]); } // On iOS and 32-bit OSX these are all just empty intrinsics, no need to @@ -460,10 +453,6 @@ mod c { ("__fe_getround", "fp_mode.c"), ("__fe_raise_inexact", "fp_mode.c"), ]); - - if cfg.target_os != "windows" && cfg.target_os != "cygwin" { - sources.extend(&[("__multc3", "multc3.c")]); - } } if cfg.target_arch == "mips" || cfg.target_arch == "riscv32" || cfg.target_arch == "riscv64" diff --git a/library/compiler-builtins/compiler-builtins/src/float/add.rs b/library/compiler-builtins/compiler-builtins/src/float/add.rs index 69de07372f16e..6503bc37dd75f 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/add.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/add.rs @@ -195,19 +195,17 @@ intrinsics! { add(a, b) } - #[aapcs_on_arm] #[arm_aeabi_alias = __aeabi_fadd] pub extern "C" fn __addsf3(a: f32, b: f32) -> f32 { add(a, b) } - #[aapcs_on_arm] #[arm_aeabi_alias = __aeabi_dadd] pub extern "C" fn __adddf3(a: f64, b: f64) -> f64 { add(a, b) } - #[ppc_alias = __addkf3] + #[ppc_name = __addkf3] #[cfg(f128_enabled)] pub extern "C" fn __addtf3(a: f128, b: f128) -> f128 { add(a, b) diff --git a/library/compiler-builtins/compiler-builtins/src/float/cmp.rs b/library/compiler-builtins/compiler-builtins/src/float/cmp.rs index 243c9c767f61b..a5ce9a2113b4d 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/cmp.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/cmp.rs @@ -227,37 +227,37 @@ intrinsics! { #[cfg(f128_enabled)] intrinsics! { - #[ppc_alias = __lekf2] + #[ppc_name = __lekf2] pub extern "C" fn __letf2(a: f128, b: f128) -> crate::float::cmp::CmpResult { cmp(a, b).to_default_cmp_result() } - #[ppc_alias = __gekf2] + #[ppc_name = __gekf2] pub extern "C" fn __getf2(a: f128, b: f128) -> crate::float::cmp::CmpResult { cmp(a, b).to_gt_ge_cmp_result() } - #[ppc_alias = __unordkf2] + #[ppc_name = __unordkf2] pub extern "C" fn __unordtf2(a: f128, b: f128) -> crate::float::cmp::CmpResult { unord(a, b) as crate::float::cmp::CmpResult } - #[ppc_alias = __eqkf2] + #[ppc_name = __eqkf2] pub extern "C" fn __eqtf2(a: f128, b: f128) -> crate::float::cmp::CmpResult { cmp(a, b).to_default_cmp_result() } - #[ppc_alias = __ltkf2] + #[ppc_name = __ltkf2] pub extern "C" fn __lttf2(a: f128, b: f128) -> crate::float::cmp::CmpResult { cmp(a, b).to_default_cmp_result() } - #[ppc_alias = __nekf2] + #[ppc_name = __nekf2] pub extern "C" fn __netf2(a: f128, b: f128) -> crate::float::cmp::CmpResult { cmp(a, b).to_default_cmp_result() } - #[ppc_alias = __gtkf2] + #[ppc_name = __gtkf2] pub extern "C" fn __gttf2(a: f128, b: f128) -> crate::float::cmp::CmpResult { cmp(a, b).to_gt_ge_cmp_result() } diff --git a/library/compiler-builtins/compiler-builtins/src/float/complex/div.rs b/library/compiler-builtins/compiler-builtins/src/float/complex/div.rs new file mode 100644 index 0000000000000..4a8d6b7231c67 --- /dev/null +++ b/library/compiler-builtins/compiler-builtins/src/float/complex/div.rs @@ -0,0 +1,74 @@ +use core::num::Complex; + +use crate::math::libm_math::generic::{fmax, ilogb, scalbn}; +use crate::support::{CastInto, Float}; + +/// Returns the quotient of `(a + ib)` and `(c + id)`. +/// +/// This implementation uses the standard formula, but has special behavior when the output +/// of that formula has both a real and imaginary component that are NaN. +fn complex_div(mut a: F, mut b: F, mut c: F, mut d: F) -> Complex +where + u32: CastInto, +{ + let max = fmax(c.abs(), d.abs()); + let mut ilogbw = 0; + if max.is_finite() && max != F::ZERO { + ilogbw = ilogb(max); + c = scalbn(c, -ilogbw); + d = scalbn(d, -ilogbw); + } + + let denom = c * c + d * d; + let mut z = Complex::new( + scalbn((a * c + b * d) / denom, -ilogbw), + scalbn((b * c - a * d) / denom, -ilogbw), + ); + + // The fast path: exit when at least one component is not NaN. + if !(z.re.is_nan() && z.im.is_nan()) { + return z; + } + + let signed_unit_if_inf = |x: F| { + let mag = if x.is_infinite() { F::ONE } else { F::ZERO }; + mag.copysign(x) + }; + + if denom == F::ZERO && (!a.is_nan() || !b.is_nan()) { + z.re = F::INFINITY.copysign(c) * a; + z.im = F::INFINITY.copysign(c) * b; + } else if (a.is_infinite() || b.is_infinite()) && c.is_finite() && d.is_finite() { + a = signed_unit_if_inf(a); + b = signed_unit_if_inf(b); + z.re = F::INFINITY * (a * c + b * d); + z.im = F::INFINITY * (b * c - a * d); + } else if max.is_infinite() && a.is_finite() && b.is_finite() { + c = signed_unit_if_inf(c); + d = signed_unit_if_inf(d); + z.re = F::ZERO * (a * c + b * d); + z.im = F::ZERO * (b * c - a * d); + } + + z +} + +intrinsics! { + #[cfg(all(f16_enabled, not(x86_no_sse2)))] + pub extern "C" fn __rust_divhc3(a: f16, b: f16, c: f16, d: f16) -> core::num::Complex { + complex_div(a, b, c, d) + } + + pub extern "C" fn __rust_divsc3(a: f32, b: f32, c: f32, d: f32) -> core::num::Complex { + complex_div(a, b, c, d) + } + + pub extern "C" fn __rust_divdc3(a: f64, b: f64, c: f64, d: f64) -> core::num::Complex { + complex_div(a, b, c, d) + } + + #[cfg(f128_enabled)] + pub extern "C" fn __rust_divtc3(a: f128, b: f128, c: f128, d: f128) -> core::num::Complex { + complex_div(a, b, c, d) + } +} diff --git a/library/compiler-builtins/compiler-builtins/src/float/complex/mod.rs b/library/compiler-builtins/compiler-builtins/src/float/complex/mod.rs new file mode 100644 index 0000000000000..5e67fbb85b206 --- /dev/null +++ b/library/compiler-builtins/compiler-builtins/src/float/complex/mod.rs @@ -0,0 +1,2 @@ +pub mod div; +pub mod mul; diff --git a/library/compiler-builtins/compiler-builtins/src/float/complex/mul.rs b/library/compiler-builtins/compiler-builtins/src/float/complex/mul.rs new file mode 100644 index 0000000000000..66c5d42baab3b --- /dev/null +++ b/library/compiler-builtins/compiler-builtins/src/float/complex/mul.rs @@ -0,0 +1,80 @@ +use core::num::Complex; + +use crate::support::Float; + +/// Returns the product of `a + ib` and `c + id`. +/// +/// The standard formula is `(ac - bd) + (ad + bc)i`, but this function has custom behavior when +/// both the real and imaginary components of that expression are NaN. +fn complex_mul(mut a: F, mut b: F, mut c: F, mut d: F) -> Complex { + let ac = a * c; + let bd = b * d; + let ad = a * d; + let bc = b * c; + + let z = Complex::new(ac - bd, ad + bc); + + // The fast path: exit when at least one component is not NaN. + if !(z.re.is_nan() && z.im.is_nan()) { + return z; + } + + let zero_if_nan = |x: F| if x.is_nan() { F::ZERO.copysign(x) } else { x }; + + let signed_unit_if_inf = |x: F| { + let mag = if x.is_infinite() { F::ONE } else { F::ZERO }; + mag.copysign(x) + }; + + let mut recalc = false; + + if a.is_infinite() || b.is_infinite() { + a = signed_unit_if_inf(a); + b = signed_unit_if_inf(b); + c = zero_if_nan(c); + d = zero_if_nan(d); + recalc = true; + } + + if c.is_infinite() || d.is_infinite() { + c = signed_unit_if_inf(c); + d = signed_unit_if_inf(d); + a = zero_if_nan(a); + b = zero_if_nan(b); + recalc = true; + } + + if !recalc && (ac.is_infinite() || bd.is_infinite() || ad.is_infinite() || bc.is_infinite()) { + a = zero_if_nan(a); + b = zero_if_nan(b); + c = zero_if_nan(c); + d = zero_if_nan(d); + recalc = true; + } + + if !recalc { + return z; + } + + Complex::new(F::INFINITY * (a * c - b * d), F::INFINITY * (a * d + b * c)) +} + +intrinsics! { + #[cfg(all(f16_enabled, not(x86_no_sse2)))] + pub extern "C" fn __rust_mulhc3(a: f16, b: f16, c: f16, d: f16) -> core::num::Complex { + complex_mul(a, b, c, d) + } + + pub extern "C" fn __rust_mulsc3(a: f32, b: f32, c: f32, d: f32) -> core::num::Complex { + complex_mul(a, b, c, d) + } + + pub extern "C" fn __rust_muldc3(a: f64, b: f64, c: f64, d: f64) -> core::num::Complex { + complex_mul(a, b, c, d) + } + + #[cfg(f128_enabled)] + pub extern "C" fn __rust_multc3(a: f128, b: f128, c: f128, d: f128) -> core::num::Complex { + complex_mul(a, b, c, d) + } +} diff --git a/library/compiler-builtins/compiler-builtins/src/float/conv.rs b/library/compiler-builtins/compiler-builtins/src/float/conv.rs index 6193aa416e222..13a0ed4fcd39f 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/conv.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/conv.rs @@ -248,19 +248,19 @@ intrinsics! { f64::from_bits(int_to_float::u128_to_f64_bits((u128::from(hi) << 64) | u128::from(lo))) } - #[ppc_alias = __floatunsikf] + #[ppc_name = __floatunsikf] #[cfg(f128_enabled)] pub extern "C" fn __floatunsitf(i: u32) -> f128 { f128::from_bits(int_to_float::u32_to_f128_bits(i)) } - #[ppc_alias = __floatundikf] + #[ppc_name = __floatundikf] #[cfg(f128_enabled)] pub extern "C" fn __floatunditf(i: u64) -> f128 { f128::from_bits(int_to_float::u64_to_f128_bits(i)) } - #[ppc_alias = __floatuntikf] + #[ppc_name = __floatuntikf] #[cfg(f128_enabled)] pub extern "C" fn __floatuntitf(i: u128) -> f128 { f128::from_bits(int_to_float::u128_to_f128_bits(i)) @@ -309,19 +309,19 @@ intrinsics! { int_to_float::signed((i128::from(hi) << 64) | i128::from(lo), int_to_float::u128_to_f64_bits) } - #[ppc_alias = __floatsikf] + #[ppc_name = __floatsikf] #[cfg(f128_enabled)] pub extern "C" fn __floatsitf(i: i32) -> f128 { int_to_float::signed(i, int_to_float::u32_to_f128_bits) } - #[ppc_alias = __floatdikf] + #[ppc_name = __floatdikf] #[cfg(f128_enabled)] pub extern "C" fn __floatditf(i: i64) -> f128 { int_to_float::signed(i, int_to_float::u64_to_f128_bits) } - #[ppc_alias = __floattikf] + #[ppc_name = __floattikf] #[cfg(f128_enabled)] pub extern "C" fn __floattitf(i: i128) -> f128 { int_to_float::signed(i, int_to_float::u128_to_f128_bits) @@ -439,19 +439,19 @@ intrinsics! { float_to_unsigned_int(f) } - #[ppc_alias = __fixunskfsi] + #[ppc_name = __fixunskfsi] #[cfg(f128_enabled)] pub extern "C" fn __fixunstfsi(f: f128) -> u32 { float_to_unsigned_int(f) } - #[ppc_alias = __fixunskfdi] + #[ppc_name = __fixunskfdi] #[cfg(f128_enabled)] pub extern "C" fn __fixunstfdi(f: f128) -> u64 { float_to_unsigned_int(f) } - #[ppc_alias = __fixunskfti] + #[ppc_name = __fixunskfti] #[cfg(f128_enabled)] pub extern "C" fn __fixunstfti(f: f128) -> u128 { float_to_unsigned_int(f) @@ -488,19 +488,19 @@ intrinsics! { float_to_signed_int(f) } - #[ppc_alias = __fixkfsi] + #[ppc_name = __fixkfsi] #[cfg(f128_enabled)] pub extern "C" fn __fixtfsi(f: f128) -> i32 { float_to_signed_int(f) } - #[ppc_alias = __fixkfdi] + #[ppc_name = __fixkfdi] #[cfg(f128_enabled)] pub extern "C" fn __fixtfdi(f: f128) -> i64 { float_to_signed_int(f) } - #[ppc_alias = __fixkfti] + #[ppc_name = __fixkfti] #[cfg(f128_enabled)] pub extern "C" fn __fixtfti(f: f128) -> i128 { float_to_signed_int(f) diff --git a/library/compiler-builtins/compiler-builtins/src/float/div.rs b/library/compiler-builtins/compiler-builtins/src/float/div.rs index 419d8ad5e7061..1438ca687be0d 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/div.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/div.rs @@ -615,7 +615,7 @@ intrinsics! { div(a, b) } - #[ppc_alias = __divkf3] + #[ppc_name = __divkf3] #[cfg(f128_enabled)] pub extern "C" fn __divtf3(a: f128, b: f128) -> f128 { div(a, b) diff --git a/library/compiler-builtins/compiler-builtins/src/float/extend.rs b/library/compiler-builtins/compiler-builtins/src/float/extend.rs index 58038ce57f834..b0f5cdd6534de 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/extend.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/extend.rs @@ -69,7 +69,6 @@ where } intrinsics! { - #[aapcs_on_arm] #[arm_aeabi_alias = __aeabi_f2d] pub extern "C" fn __extendsfdf2(a: f32) -> f64 { extend(a) @@ -77,7 +76,6 @@ intrinsics! { } intrinsics! { - #[aapcs_on_arm] #[apple_f16_arg_abi] #[arm_aeabi_alias = __aeabi_h2f] #[cfg(f16_enabled)] @@ -85,36 +83,31 @@ intrinsics! { extend(a) } - #[aapcs_on_arm] #[apple_f16_arg_abi] #[cfg(f16_enabled)] pub extern "C" fn __gnu_h2f_ieee(a: f16) -> f32 { extend(a) } - #[aapcs_on_arm] #[apple_f16_arg_abi] #[cfg(f16_enabled)] pub extern "C" fn __extendhfdf2(a: f16) -> f64 { extend(a) } - #[aapcs_on_arm] - #[ppc_alias = __extendhfkf2] + #[ppc_name = __extendhfkf2] #[cfg(all(f16_enabled, f128_enabled))] pub extern "C" fn __extendhftf2(a: f16) -> f128 { extend(a) } - #[aapcs_on_arm] - #[ppc_alias = __extendsfkf2] + #[ppc_name = __extendsfkf2] #[cfg(f128_enabled)] pub extern "C" fn __extendsftf2(a: f32) -> f128 { extend(a) } - #[aapcs_on_arm] - #[ppc_alias = __extenddfkf2] + #[ppc_name = __extenddfkf2] #[cfg(f128_enabled)] pub extern "C" fn __extenddftf2(a: f64) -> f128 { extend(a) diff --git a/library/compiler-builtins/compiler-builtins/src/float/mod.rs b/library/compiler-builtins/compiler-builtins/src/float/mod.rs index 15318c4928804..df45d0603fc14 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/mod.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/mod.rs @@ -1,5 +1,6 @@ pub mod add; pub mod cmp; +pub mod complex; pub mod conv; pub mod div; pub mod extend; diff --git a/library/compiler-builtins/compiler-builtins/src/float/mul.rs b/library/compiler-builtins/compiler-builtins/src/float/mul.rs index ffba2dc41f8a0..1d5eb1a032d29 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/mul.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/mul.rs @@ -184,19 +184,17 @@ intrinsics! { mul(a, b) } - #[aapcs_on_arm] #[arm_aeabi_alias = __aeabi_fmul] pub extern "C" fn __mulsf3(a: f32, b: f32) -> f32 { mul(a, b) } - #[aapcs_on_arm] #[arm_aeabi_alias = __aeabi_dmul] pub extern "C" fn __muldf3(a: f64, b: f64) -> f64 { mul(a, b) } - #[ppc_alias = __mulkf3] + #[ppc_name = __mulkf3] #[cfg(f128_enabled)] pub extern "C" fn __multf3(a: f128, b: f128) -> f128 { mul(a, b) diff --git a/library/compiler-builtins/compiler-builtins/src/float/pow.rs b/library/compiler-builtins/compiler-builtins/src/float/pow.rs index 2c92971d31397..50a27055a849f 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/pow.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/pow.rs @@ -29,7 +29,7 @@ intrinsics! { pow(a, b) } - #[ppc_alias = __powikf2] + #[ppc_name = __powikf2] #[cfg(f128_enabled)] pub extern "C" fn __powitf2(a: f128, b: i32) -> f128 { pow(a, b) diff --git a/library/compiler-builtins/compiler-builtins/src/float/sub.rs b/library/compiler-builtins/compiler-builtins/src/float/sub.rs index 11dd3b77d5d1c..7028b2ff8a80c 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/sub.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/sub.rs @@ -16,7 +16,7 @@ intrinsics! { crate::float::add::__adddf3(a, f64::from_bits(b.to_bits() ^ f64::SIGN_MASK)) } - #[ppc_alias = __subkf3] + #[ppc_name = __subkf3] #[cfg(f128_enabled)] pub extern "C" fn __subtf3(a: f128, b: f128) -> f128 { #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] diff --git a/library/compiler-builtins/compiler-builtins/src/float/trunc.rs b/library/compiler-builtins/compiler-builtins/src/float/trunc.rs index 1a88b0649fda3..1bac7b0957cd5 100644 --- a/library/compiler-builtins/compiler-builtins/src/float/trunc.rs +++ b/library/compiler-builtins/compiler-builtins/src/float/trunc.rs @@ -114,7 +114,6 @@ where } intrinsics! { - #[aapcs_on_arm] #[arm_aeabi_alias = __aeabi_d2f] pub extern "C" fn __truncdfsf2(a: f64) -> f32 { trunc(a) @@ -122,7 +121,6 @@ intrinsics! { } intrinsics! { - #[aapcs_on_arm] #[apple_f16_ret_abi] #[arm_aeabi_alias = __aeabi_f2h] #[cfg(f16_enabled)] @@ -130,14 +128,12 @@ intrinsics! { trunc(a) } - #[aapcs_on_arm] #[apple_f16_ret_abi] #[cfg(f16_enabled)] pub extern "C" fn __gnu_f2h_ieee(a: f32) -> f16 { trunc(a) } - #[aapcs_on_arm] #[apple_f16_ret_abi] #[arm_aeabi_alias = __aeabi_d2h] #[cfg(f16_enabled)] @@ -145,22 +141,19 @@ intrinsics! { trunc(a) } - #[aapcs_on_arm] - #[ppc_alias = __trunckfhf2] + #[ppc_name = __trunckfhf2] #[cfg(all(f16_enabled, f128_enabled))] pub extern "C" fn __trunctfhf2(a: f128) -> f16 { trunc(a) } - #[aapcs_on_arm] - #[ppc_alias = __trunckfsf2] + #[ppc_name = __trunckfsf2] #[cfg(f128_enabled)] pub extern "C" fn __trunctfsf2(a: f128) -> f32 { trunc(a) } - #[aapcs_on_arm] - #[ppc_alias = __trunckfdf2] + #[ppc_name = __trunckfdf2] #[cfg(f128_enabled)] pub extern "C" fn __trunctfdf2(a: f128) -> f64 { trunc(a) diff --git a/library/compiler-builtins/compiler-builtins/src/lib.rs b/library/compiler-builtins/compiler-builtins/src/lib.rs index 0eb9a73b663f6..5dfc6733befec 100644 --- a/library/compiler-builtins/compiler-builtins/src/lib.rs +++ b/library/compiler-builtins/compiler-builtins/src/lib.rs @@ -6,6 +6,7 @@ #![feature(asm_experimental_arch)] #![feature(cfg_target_has_atomic)] #![feature(compiler_builtins)] +#![feature(complex_numbers)] #![feature(core_intrinsics)] #![feature(linkage)] #![feature(repr_simd)] diff --git a/library/compiler-builtins/compiler-builtins/src/macros.rs b/library/compiler-builtins/compiler-builtins/src/macros.rs index f6736b7bc7fda..42895773d3d92 100644 --- a/library/compiler-builtins/compiler-builtins/src/macros.rs +++ b/library/compiler-builtins/compiler-builtins/src/macros.rs @@ -38,13 +38,12 @@ /// /// A quick overview of attributes supported right now are: /// +// FIXME: Add missing attributes. /// * `maybe_use_optimized_c_shim` - indicates that the Rust implementation is /// ignored if an optimized C version was compiled. -/// * `aapcs_on_arm` - forces the ABI of the function to be `"aapcs"` on ARM and -/// the specified ABI everywhere else. /// * `arm_aeabi_alias` - handles the "aliasing" of various intrinsics on ARM /// their otherwise typical names to other prefixed ones. -/// * `ppc_alias` - changes the name of the symbol on PowerPC platforms without +/// * `ppc_name` - changes the name of the symbol on PowerPC platforms without /// changing any other behavior. This is mostly for `f128`, which is `tf` on /// most platforms but `kf` on PowerPC. macro_rules! intrinsics { @@ -168,38 +167,7 @@ macro_rules! intrinsics { intrinsics!($($rest)*); ); - // We recognize the `#[aapcs_on_arm]` attribute here and generate the - // same intrinsic but force it to have the `"aapcs"` calling convention on - // ARM and `"C"` elsewhere. - ( - #[aapcs_on_arm] - $(#[$($attr:tt)*])* - pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { - $($body:tt)* - } - - $($rest:tt)* - ) => ( - #[cfg(target_arch = "arm")] - intrinsics! { - $(#[$($attr)*])* - pub extern "aapcs" fn $name( $($argname: $ty),* ) $(-> $ret)? { - $($body)* - } - } - - #[cfg(not(target_arch = "arm"))] - intrinsics! { - $(#[$($attr)*])* - pub extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { - $($body)* - } - } - - intrinsics!($($rest)*); - ); - - // `arm_aeabi_alias` would conflict with `f16_apple_{arg,ret}_abi` not handled here. Avoid macro ambiguity by combining in a + // `arm_aeabi_alias` would conflict with `apple_f16_{arg,ret}_abi` not handled here. Avoid macro ambiguity by combining in a // single `#[]`. ( #[apple_f16_arg_abi] @@ -350,9 +318,9 @@ macro_rules! intrinsics { ); // PowerPC usually uses `kf` rather than `tf` for `f128`. This is just an easy - // way to add an alias on those targets. + // way to change the name on those targets. ( - #[ppc_alias = $alias:ident] + #[ppc_name = $ppc_name:ident] $(#[$($attr:tt)*])* pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { $($body:tt)* @@ -371,7 +339,7 @@ macro_rules! intrinsics { #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] intrinsics! { $(#[$($attr)*])* - pub extern $abi fn $alias( $($argname: $ty),* ) $(-> $ret)? { + pub extern $abi fn $ppc_name( $($argname: $ty),* ) $(-> $ret)? { $($body)* } } diff --git a/library/compiler-builtins/libm/src/math/mod.rs b/library/compiler-builtins/libm/src/math/mod.rs index 50b42e0c07972..51a3417c8b151 100644 --- a/library/compiler-builtins/libm/src/math/mod.rs +++ b/library/compiler-builtins/libm/src/math/mod.rs @@ -72,7 +72,7 @@ cfg_select_nofmt! { pub mod generic; } _ => { - mod generic; + pub(crate) mod generic; } } diff --git a/library/compiler-builtins/libm/src/math/support/float_traits.rs b/library/compiler-builtins/libm/src/math/support/float_traits.rs index f802f4be6c2d7..1bded45ea930e 100644 --- a/library/compiler-builtins/libm/src/math/support/float_traits.rs +++ b/library/compiler-builtins/libm/src/math/support/float_traits.rs @@ -153,6 +153,12 @@ pub trait Float: /// Returns true if the value is +inf or -inf. fn is_infinite(self) -> bool; + /// Returns true if this number is neither infinite nor NaN. + #[allow(dead_code)] + fn is_finite(self) -> bool { + self.abs() < Self::INFINITY + } + /// Returns true if the sign is negative. Extracts the sign bit regardless of zero or NaN. fn is_sign_negative(self) -> bool; diff --git a/library/compiler-builtins/libm/src/math/support/hex_float.rs b/library/compiler-builtins/libm/src/math/support/hex_float.rs index 607ba7964023b..5040c3fae7933 100644 --- a/library/compiler-builtins/libm/src/math/support/hex_float.rs +++ b/library/compiler-builtins/libm/src/math/support/hex_float.rs @@ -698,62 +698,48 @@ mod parse_tests { } } } - // HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to - // hide them from the AST. + #[test] #[cfg(f16_enabled)] - macro_rules! f16_tests { - () => { - #[test] - fn test_f16() { - let checks = [ - ("0x.1234p+16", (0x1234 as f16).to_bits()), - ("0x1.234p+12", (0x1234 as f16).to_bits()), - ("0x12.34p+8", (0x1234 as f16).to_bits()), - ("0x123.4p+4", (0x1234 as f16).to_bits()), - ("0x1234p+0", (0x1234 as f16).to_bits()), - ("0x1234.p+0", (0x1234 as f16).to_bits()), - ("0x1234.0p+0", (0x1234 as f16).to_bits()), - ("0x1.ffcp+15", f16::MAX.to_bits()), - ("0x1.0p+1", 2.0f16.to_bits()), - ("0x1.0p+0", 1.0f16.to_bits()), - ("0x1.ffp+8", 0x5ffc), - ("+0x1.ffp+8", 0x5ffc), - ("0x1p+0", 0x3c00), - ("0x1.998p-4", 0x2e66), - ("0x1.9p+6", 0x5640), - ("0x0.0p0", 0.0f16.to_bits()), - ("-0x0.0p0", (-0.0f16).to_bits()), - ("0x1.0p0", 1.0f16.to_bits()), - ("0x1.998p-4", (0.1f16).to_bits()), - ("-0x1.998p-4", (-0.1f16).to_bits()), - ("0x0.123p-12", 0x0123), - ("0x1p-24", 0x0001), - ("nan", f16::NAN.to_bits()), - ("-nan", (-f16::NAN).to_bits()), - ("inf", f16::INFINITY.to_bits()), - ("-inf", f16::NEG_INFINITY.to_bits()), - ]; - for (s, exp) in checks { - println!("parsing {s}"); - assert!(rounding_properties(s).is_ok()); - let act = hf16(s).to_bits(); - assert_eq!( - act, exp, - "parsing {s}: {act:#06x} != {exp:#06x}\nact: {act:#018b}\nexp: {exp:#018b}" - ); - } - } - - #[test] - fn test_macros_f16() { - assert_eq!(hf16!("0x1.ffp+8").to_bits(), 0x5ffc_u16); - } - }; + fn test_f16() { + let checks = [ + ("0x.1234p+16", (0x1234 as f16).to_bits()), + ("0x1.234p+12", (0x1234 as f16).to_bits()), + ("0x12.34p+8", (0x1234 as f16).to_bits()), + ("0x123.4p+4", (0x1234 as f16).to_bits()), + ("0x1234p+0", (0x1234 as f16).to_bits()), + ("0x1234.p+0", (0x1234 as f16).to_bits()), + ("0x1234.0p+0", (0x1234 as f16).to_bits()), + ("0x1.ffcp+15", f16::MAX.to_bits()), + ("0x1.0p+1", 2.0f16.to_bits()), + ("0x1.0p+0", 1.0f16.to_bits()), + ("0x1.ffp+8", 0x5ffc), + ("+0x1.ffp+8", 0x5ffc), + ("0x1p+0", 0x3c00), + ("0x1.998p-4", 0x2e66), + ("0x1.9p+6", 0x5640), + ("0x0.0p0", 0.0f16.to_bits()), + ("-0x0.0p0", (-0.0f16).to_bits()), + ("0x1.0p0", 1.0f16.to_bits()), + ("0x1.998p-4", (0.1f16).to_bits()), + ("-0x1.998p-4", (-0.1f16).to_bits()), + ("0x0.123p-12", 0x0123), + ("0x1p-24", 0x0001), + ("nan", f16::NAN.to_bits()), + ("-nan", (-f16::NAN).to_bits()), + ("inf", f16::INFINITY.to_bits()), + ("-inf", f16::NEG_INFINITY.to_bits()), + ]; + for (s, exp) in checks { + println!("parsing {s}"); + assert!(rounding_properties(s).is_ok()); + let act = hf16(s).to_bits(); + assert_eq!( + act, exp, + "parsing {s}: {act:#06x} != {exp:#06x}\nact: {act:#018b}\nexp: {exp:#018b}" + ); + } } - #[cfg(f16_enabled)] - f16_tests!(); - #[test] fn test_f32() { let checks = [ @@ -840,61 +826,56 @@ mod parse_tests { } } - // HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to - // hide them from the AST. + #[test] #[cfg(f128_enabled)] - macro_rules! f128_tests { - () => { - #[test] - fn test_f128() { - let checks = [ - ("0x.1234p+16", (0x1234 as f128).to_bits()), - ("0x1.234p+12", (0x1234 as f128).to_bits()), - ("0x12.34p+8", (0x1234 as f128).to_bits()), - ("0x123.4p+4", (0x1234 as f128).to_bits()), - ("0x1234p+0", (0x1234 as f128).to_bits()), - ("0x1234.p+0", (0x1234 as f128).to_bits()), - ("0x1234.0p+0", (0x1234 as f128).to_bits()), - ("0x1.ffffffffffffffffffffffffffffp+16383", f128::MAX.to_bits()), - ("0x1.0p+1", 2.0f128.to_bits()), - ("0x1.0p+0", 1.0f128.to_bits()), - ("0x1.ffep+8", 0x4007ffe0000000000000000000000000), - ("+0x1.ffep+8", 0x4007ffe0000000000000000000000000), - ("0x1p+0", 0x3fff0000000000000000000000000000), - ("0x1.999999999999999999999999999ap-4", 0x3ffb999999999999999999999999999a), - ("0x1.9p+6", 0x40059000000000000000000000000000), - ("0x0.0p0", 0.0f128.to_bits()), - ("-0x0.0p0", (-0.0f128).to_bits()), - ("0x1.0p0", 1.0f128.to_bits()), - ("0x1.999999999999999999999999999ap-4", (0.1f128).to_bits()), - ("-0x1.999999999999999999999999999ap-4", (-0.1f128).to_bits()), - ("0x0.abcdef0123456789abcdef012345p-16382", 0x0000abcdef0123456789abcdef012345), - ("0x1p-16494", 0x00000000000000000000000000000001), - ("nan", f128::NAN.to_bits()), - ("-nan", (-f128::NAN).to_bits()), - ("inf", f128::INFINITY.to_bits()), - ("-inf", f128::NEG_INFINITY.to_bits()), - ]; - for (s, exp) in checks { - println!("parsing {s}"); - let act = hf128(s).to_bits(); - assert_eq!( - act, exp, - "parsing {s}: {act:#034x} != {exp:#034x}\nact: {act:#0130b}\nexp: {exp:#0130b}" - ); - } - } - - #[test] - fn test_macros_f128() { - assert_eq!(hf128!("0x1.ffep+8").to_bits(), 0x4007ffe0000000000000000000000000_u128); - } + fn test_f128() { + let checks = [ + ("0x.1234p+16", (0x1234 as f128).to_bits()), + ("0x1.234p+12", (0x1234 as f128).to_bits()), + ("0x12.34p+8", (0x1234 as f128).to_bits()), + ("0x123.4p+4", (0x1234 as f128).to_bits()), + ("0x1234p+0", (0x1234 as f128).to_bits()), + ("0x1234.p+0", (0x1234 as f128).to_bits()), + ("0x1234.0p+0", (0x1234 as f128).to_bits()), + ( + "0x1.ffffffffffffffffffffffffffffp+16383", + f128::MAX.to_bits(), + ), + ("0x1.0p+1", 2.0f128.to_bits()), + ("0x1.0p+0", 1.0f128.to_bits()), + ("0x1.ffep+8", 0x4007ffe0000000000000000000000000), + ("+0x1.ffep+8", 0x4007ffe0000000000000000000000000), + ("0x1p+0", 0x3fff0000000000000000000000000000), + ( + "0x1.999999999999999999999999999ap-4", + 0x3ffb999999999999999999999999999a, + ), + ("0x1.9p+6", 0x40059000000000000000000000000000), + ("0x0.0p0", 0.0f128.to_bits()), + ("-0x0.0p0", (-0.0f128).to_bits()), + ("0x1.0p0", 1.0f128.to_bits()), + ("0x1.999999999999999999999999999ap-4", (0.1f128).to_bits()), + ("-0x1.999999999999999999999999999ap-4", (-0.1f128).to_bits()), + ( + "0x0.abcdef0123456789abcdef012345p-16382", + 0x0000abcdef0123456789abcdef012345, + ), + ("0x1p-16494", 0x00000000000000000000000000000001), + ("nan", f128::NAN.to_bits()), + ("-nan", (-f128::NAN).to_bits()), + ("inf", f128::INFINITY.to_bits()), + ("-inf", f128::NEG_INFINITY.to_bits()), + ]; + for (s, exp) in checks { + println!("parsing {s}"); + let act = hf128(s).to_bits(); + assert_eq!( + act, exp, + "parsing {s}: {act:#034x} != {exp:#034x}\nact: {act:#0130b}\nexp: {exp:#0130b}" + ); } } - #[cfg(f128_enabled)] - f128_tests!(); - #[test] fn test_macros() { #[cfg(f16_enabled)] diff --git a/library/compiler-builtins/rust-version b/library/compiler-builtins/rust-version index 2f175e966812d..6c07d50b9c4b3 100644 --- a/library/compiler-builtins/rust-version +++ b/library/compiler-builtins/rust-version @@ -1 +1 @@ -2c39ff499469be916d4e45506d1afed69bbaddb7 +45f215f136e00d8a74c69afde2f71be3f16837cf diff --git a/rust-bors.toml b/rust-bors.toml index 02effccdeeeb3..527d44126bf2d 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -87,31 +87,20 @@ images = { "arm64ami" = "latest-gha-runner-ami-arm64", } jit_runner = "organization" +# Prices per hour of on-demand compute in us-east-2 (as of Aug 2026) +# See build speed estimates in https://github.com/rust-lang/simpleinfra/issues/1132 allowed_instances = [ - # AMD Zen 5 (x86_64) instances, a subset of these is used in production. - # Prices per hour of on-demand compute in us-east-2 (as of Aug 2026) - # See rough assessment of build speed for dist-x86_64-quick in https://github.com/rust-lang/simpleinfra/issues/1132 - # m8a.2x 8 vCPU, 32 GB $0.48688/hr - # c8a.4x 16 vCPU, 32 GB $0.86216/hr - # c8a.8x 32 vCPU, 64 GB $1.72432/hr - # c8a.12x 48 vCPU, 96 GB $2.58648/hr - # CodeBuild 36 vCPU $4.78799/hr - "m8a.2xlarge", - "c8a.4xlarge", - "c8a.8xlarge", - "c8a.12xlarge", + # AMD Zen 5 (x86_64) + "m8a.2xlarge", # $0.48688/hr + "c8a.4xlarge", # $0.86216/hr + "c8a.8xlarge", # $1.72432/hr + "c8a.12xlarge", # $2.58648/hr - # Graviton 4 (aarch64) instances, currently just for experimentation - "m8g.2xlarge", - "c8g.4xlarge", - "c8g.8xlarge", - "c8g.12xlarge", - - # Graviton 5 (aarch64) instances, currently just for experimentation - "m9g.2xlarge", - "c9g.4xlarge", - "c9g.8xlarge", - "c9g.12xlarge", + # Graviton (aarch64) + "m9g.2xlarge", # $0.39136/hr + "c9g.4xlarge", # $0.69312/hr + "c9g.8xlarge", # $1.38624/hr + "c9g.12xlarge", # $2.07936/hr ] # Enable unrolling of rollup member PRs after rollup merge diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 387d0b77f1af5..688d75589dd9a 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -41,24 +41,24 @@ runners: os: ubuntu-24.04-arm <<: *base-job - - &job-aarch64-linux-8c - os: ubuntu-24.04-arm64-8core-32gb + - &job-linux-x86-8c-ec2 + os: ec2-x86_64ami-m8a.2xlarge-x64-linux-$github.run_id-$github.run_attempt <<: *base-job - # Codebuild runners are provisioned in - # https://github.com/rust-lang/simpleinfra/blob/b7ddd5e6bec8a93ec30510cdddec02c5666fefe9/terragrunt/accounts/ci-prod/ci-runners/terragrunt.hcl#L2 - - &job-linux-36c-codebuild - free_disk: true - codebuild: true - os: codebuild-ubuntu-22-36c-$github.run_id-$github.run_attempt + - &job-linux-x86-16c-ec2 + os: ec2-x86_64ami-c8a.4xlarge-x64-linux-$github.run_id-$github.run_attempt <<: *base-job - &job-linux-x86-32c-ec2 os: ec2-x86_64ami-c8a.8xlarge-x64-linux-$github.run_id-$github.run_attempt <<: *base-job - - &job-linux-x86-8c-ec2 - os: ec2-x86_64ami-m8a.2xlarge-x64-linux-$github.run_id-$github.run_attempt + - &job-linux-aarch64-8c-ec2 + os: ec2-arm64ami-m9g.2xlarge-aarch64-linux-$github.run_id-$github.run_attempt + <<: *base-job + + - &job-linux-aarch64-16c-ec2 + os: ec2-arm64ami-c9g.4xlarge-aarch64-linux-$github.run_id-$github.run_attempt <<: *base-job envs: @@ -96,6 +96,11 @@ jobs: IMAGE: dist-x86_64-linux CODEGEN_BACKENDS: llvm,cranelift DOCKER_SCRIPT: dist.sh + dist-aarch64-linux: &job-dist-aarch64-linux + name: dist-aarch64-linux + env: + IMAGE: dist-aarch64-linux + CODEGEN_BACKENDS: llvm,cranelift # Jobs that run on each push to a pull request (PR). @@ -167,6 +172,17 @@ pr: try: - <<: [*job-dist-x86_64-linux, *job-linux-x86-32c-ec2] name: dist-x86_64-linux-quick + env: + IMAGE: dist-x86_64-linux + CODEGEN_BACKENDS: llvm,cranelift + DOCKER_SCRIPT: dist.sh + DIST_TRY_BUILD: 1 + - <<: [*job-dist-aarch64-linux, *job-linux-aarch64-16c-ec2] + name: dist-aarch64-linux-quick + env: + IMAGE: dist-aarch64-linux + CODEGEN_BACKENDS: llvm,cranelift + DIST_TRY_BUILD: 1 # Jobs that only run when explicitly invoked in one of the following ways: # - comment `@bors try jobs=` @@ -178,19 +194,20 @@ optional: env: IMAGE: pr-check-1 <<: *job-linux-4c - - <<: [*job-dist-x86_64-linux, *job-linux-36c-codebuild] - name: dist-x86_64-linux-codebuild - - <<: [*job-dist-x86_64-linux, *job-linux-36c-codebuild] - name: dist-x86_64-linux-quick-codebuild + # Duplicate the try jobs here so that we can run them via jobs=... + - <<: [*job-dist-x86_64-linux, *job-linux-x86-32c-ec2] + name: dist-x86_64-linux-quick env: IMAGE: dist-x86_64-linux CODEGEN_BACKENDS: llvm,cranelift DOCKER_SCRIPT: dist.sh DIST_TRY_BUILD: 1 - # We repeat the try job here so that it can be explicitly executed using `@bors try jobs`, to test - # full x64 Linux dist try builds on EC2. - - <<: [*job-dist-x86_64-linux, *job-linux-x86-32c-ec2] - name: dist-x86_64-linux-quick + - <<: [*job-dist-aarch64-linux, *job-linux-aarch64-16c-ec2] + name: dist-aarch64-linux-quick + env: + IMAGE: dist-aarch64-linux + CODEGEN_BACKENDS: llvm,cranelift + DIST_TRY_BUILD: 1 # Main CI jobs that have to be green to merge a commit into the default branch. # @@ -218,10 +235,7 @@ auto: - name: armhf-gnu <<: *job-linux-4c - - name: dist-aarch64-linux - env: - CODEGEN_BACKENDS: llvm,cranelift - <<: *job-aarch64-linux-8c + - <<: [*job-dist-aarch64-linux, *job-linux-aarch64-16c-ec2] - name: dist-android <<: *job-linux-4c @@ -298,7 +312,7 @@ auto: - name: dist-x86_64-illumos <<: *job-linux-4c - - <<: [*job-dist-x86_64-linux, *job-linux-x86-32c-ec2] + - <<: [*job-dist-x86_64-linux, *job-linux-x86-16c-ec2] - name: dist-x86_64-linux-alt env: diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md index 6a450909eb8a4..d5a218dfa87c0 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-structs.md @@ -152,7 +152,7 @@ tcx.dcx().emit_err(FieldAlreadyDeclared { - _Applied to struct or struct fields of type `Span`, `Option<()>`, `bool`, or `()`._ - Adds a warning subdiagnostic. - Value is the warning's message. -- `#[suggestion{,_hidden,_short,_verbose}("message", code = "...", applicability = "...")]` +- `#[suggestion("message", code = "...", applicability = "...", style = "...")]` (_Optional_) - _Applied to `(Span, MachineApplicability)` or `Span` fields._ - Adds a suggestion subdiagnostic. @@ -165,6 +165,9 @@ tcx.dcx().emit_err(FieldAlreadyDeclared { - `applicability = "..."` (_Optional_) - String which must be one of `machine-applicable`, `maybe-incorrect`, `has-placeholders` or `unspecified`. + - `style = "..."` (_Optional_) + - Value is the style of the suggestion. + - String which must be one of `normal`, `short`, `hidden`, `verbose` or `tool-only`. - `#[subdiagnostic]` - _Applied to a type that implements `Subdiagnostic` (from `#[derive(Subdiagnostic)]`)._ - Adds the subdiagnostic represented by the subdiagnostic struct. @@ -209,7 +212,7 @@ Each `Subdiagnostic` should have one attribute applied to the struct or each var - `#[note(..)]` for defining a note - `#[help(..)]` for defining a help - `#[warning(..)]` for defining a warning -- `#[suggestion{,_hidden,_short,_verbose}(..)]` for defining a suggestion +- `#[suggestion(..)]` for defining a suggestion All of the above must provide a diagnostic message as the first positional argument. See [translation documentation](./translation.md) to learn more about how @@ -305,7 +308,7 @@ Additionally, subdiagnostics can access arguments from the main diagnostic with - Message (_Mandatory_) - The diagnostic message that will be shown to the user. - See [translation documentation](./translation.md). -- `#[suggestion{,_hidden,_short,_verbose}("message", code = "...", applicability = "...")]` +- `#[suggestion("message", code = "...", applicability = "...", style = "...")]` - _Applied to struct or enum variant. Mutually exclusive with struct/enum variant attributes._ - _Mandatory_ @@ -324,13 +327,22 @@ Additionally, subdiagnostics can access arguments from the main diagnostic with - `maybe-incorrect` - `has-placeholders` - `unspecified` -- `#[multipart_suggestion{,_hidden,_short,_verbose}("message", applicability = "...")]` + - `style = "..."` (_Optional_) + - Value is the style of the suggestion. + - String which must be one of: + - `normal` (the default) + - `short` + - `hidden` + - `verbose` + - `tool-only` +- `#[multipart_suggestion("message", applicability = "...", style = "...")]` - _Applied to struct or enum variant. Mutually exclusive with struct/enum variant attributes._ - _Mandatory_ - Defines the type to be representing a multipart suggestion. - Message (_Mandatory_): see `#[suggestion]` - `applicability = "..."` (_Optional_): see `#[suggestion]` + - `style = "..."` (_Optional_): see `#[suggestion]` - `#[primary_span]` (_Mandatory_ for labels and suggestions; _optional_ otherwise; not applicable to multipart suggestions) - _Applied to `Span` fields._ diff --git a/src/doc/rustc-dev-guide/src/feature-gate-check.md b/src/doc/rustc-dev-guide/src/feature-gate-check.md index 0b4fc0cd680c0..7726122b02f38 100644 --- a/src/doc/rustc-dev-guide/src/feature-gate-check.md +++ b/src/doc/rustc-dev-guide/src/feature-gate-check.md @@ -100,7 +100,7 @@ Beyond syntax, rustc also gates attributes and `cfg` options. ### Built-in attributes -- [`rustc_ast_passes::check_attribute`] inspects attributes against `BUILTIN_ATTRIBUTE_MAP`. +- [`rustc_ast_passes::check_attribute`] inspects attributes against `BUILTIN_ATTRIBUTE_SET`. - If the attribute is `AttributeGate::Gated` and the feature isn’t enabled, `feature_err` is emitted. diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index d13a3fdb864bf..012c4997db9c1 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -358,7 +358,8 @@ pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { ty::ConstKind::Alias(_, ty::AliasConst { kind, .. }) => { let def_id: DefId = match kind { ty::AliasConstKind::Projection { def_id } => def_id.into(), - ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::InherentSelf { def_id } => def_id.into(), + ty::AliasConstKind::InherentImpl { def_id } => def_id.into(), ty::AliasConstKind::Free { def_id } => def_id.into(), ty::AliasConstKind::Anon { def_id } => def_id.into(), }; diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index ca9e9719add48..7ea734e7ef1c0 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -41,7 +41,11 @@ jobs: proc-macro-srv: if: github.repository == 'rust-lang/rust-analyzer' name: proc-macro-srv - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-latest, windows-latest] steps: - name: Checkout repository @@ -206,11 +210,8 @@ jobs: - name: Install Rust toolchain run: | - # FIXME: Pin nightly due to a regression in miri on nightly-2026-02-12. - # See https://github.com/rust-lang/miri/issues/4855. - # Revert to plain `nightly` once this is fixed upstream. - rustup toolchain install nightly-2026-02-10 - rustup default nightly-2026-02-10 + rustup toolchain install nightly + rustup default nightly rustup component add miri # - name: Cache Dependencies diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index 7d6e0199abe0b..85ff3d08a78a3 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -252,9 +252,7 @@ jobs: matrix: include: - cmd: vsce - pat: MARKETPLACE_TOKEN - cmd: ovsx - pat: OPENVSX_TOKEN steps: - name: Install Nodejs uses: actions/setup-node@v6 @@ -277,6 +275,8 @@ jobs: - name: Publish Extension if: github.repository == 'rust-lang/rust-analyzer' + env: + PUBLISH_PAT: ${{ (matrix.cmd == 'vsce' && secrets.MARKETPLACE_TOKEN) || (matrix.cmd == 'ovsx' && secrets.OPENVSX_TOKEN) }} working-directory: ./editors/code - run: npx ${{ matrix.cmd }} publish --skip-duplicate --pat ${{ secrets[matrix.pat] }} --packagePath ../../dist/rust-analyzer-*.vsix ${{ github.ref != 'refs/heads/release' && '--pre-release' || '' }} + run: npx ${{ matrix.cmd }} publish --skip-duplicate --pat "$PUBLISH_PAT" --packagePath ../../dist/rust-analyzer-*.vsix ${{ github.ref != 'refs/heads/release' && '--pre-release' || '' }} timeout-minutes: 2 diff --git a/src/tools/rust-analyzer/AI_POLICY.md b/src/tools/rust-analyzer/AI_POLICY.md index afe7f82a0a11c..59db4a07b4fb5 100644 --- a/src/tools/rust-analyzer/AI_POLICY.md +++ b/src/tools/rust-analyzer/AI_POLICY.md @@ -30,6 +30,14 @@ E-easy issues are usually easier for maintainers to just fix directly than write AI *may* be used to understand the codebase for E-easy+E-has-instructions contributions, but not to write any code. -This policy was adapted from [uv's AI policy]. +When using AI to author changes to *analysis* - the code responsible for analyzing Rust code and not for implementing IDE features, including +but not limited to: type inference, MIR, name resolution, macro expansion - generally anything in the crates `parser`, `mbe`, `hir-expand`, `hir-def`, `hir-ty`, +although there are exceptions; **including when using AI only to analyze bugs and not to write code**, you are required to include a citation +of the rustc code responsible for the change you did, along with an explanation of how your change follows from it in case this is not immediately clear. + +The reason for that is that it is almost impossible to be fully correct in analysis if we implement things differently from rustc. We should not guess +how to fix bugs in analysis without looking at the rustc code. + +This policy was inspired by [uv's AI policy]. [uv's AI policy]: https://github.com/astral-sh/.github/blob/c5187e200db51bfe11d56e13053d29bd3793fdd8/AI_POLICY.md diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 983cfaf992dfe..5835ed9e552e7 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1890,7 +1890,7 @@ dependencies = [ "paths", "proc-macro-test", "span", - "temp-dir", + "stdx", ] [[package]] @@ -1966,7 +1966,6 @@ dependencies = [ "serde_json", "span", "stdx", - "temp-dir", "toml", "toolchain", "tracing", @@ -2752,12 +2751,6 @@ dependencies = [ "tt", ] -[[package]] -name = "temp-dir" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016ef9739649996fcc983b9c588fe3d557cf216d4d98503ce1b057ab5a66d689" - [[package]] name = "tempfile" version = "3.27.0" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index b798e364555df..d77e89df45e44 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"] resolver = "2" [workspace.package] -rust-version = "1.95" +rust-version = "1.98" edition = "2024" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] @@ -143,7 +143,6 @@ smallvec = { version = "1.15.1", features = [ "const_generics", ] } smol_str = "0.3.2" -temp-dir = "0.2.0" text-size = "1.1.1" toml = "1.1.2" tracing = { version = "0.1.41", default-features = false, features = ["std"] } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index 3dc278fb1a703..f91140ee8f934 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs @@ -31,7 +31,7 @@ use tt::{TextRange, TextSize}; use crate::{macro_call_as_call_id, nameres::MacroSubNs, resolver::Resolver}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) struct DocsSourceMapLine { +struct DocsSourceMapLine { /// The offset in [`Docs::docs`]. string_offset: TextSize, /// The offset in the AST of the text. `None` for macro-expanded doc strings @@ -62,6 +62,20 @@ pub struct Docs { macro_calls: ThinVec<(AstId, MacroCallId)>, } +#[derive(Clone, Copy)] +enum DocCommentKind { + /// `///` etc.. + Sugared(ast::CommentShape), + /// `#[doc = ""]`. + Desugared, +} + +#[derive(Default)] +struct Indent { + lines: Vec>, + seen_sugared: bool, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum IsInnerDoc { No, @@ -199,51 +213,138 @@ impl Docs { )); } - fn extend_with_doc_comment(&mut self, comment: ast::Comment, indent: &mut usize) { + fn extend_with_doc_comment(&mut self, comment: ast::Comment, indent: &mut Indent) { let Some((doc, offset)) = comment.doc_comment() else { return }; - self.extend_with_doc_str(doc, comment.syntax().text_range().start() + offset, indent); + let offset = comment.syntax().text_range().start() + offset; + self.extend_with_doc_str( + doc, + offset, + DocCommentKind::Sugared(comment.kind().shape), + indent, + ); } - fn extend_with_doc_attr(&mut self, value: ast::String, indent: &mut usize) { + fn extend_with_doc_attr(&mut self, value: ast::String, indent: &mut Indent) { let Some(value_offset) = value.text_range_between_quotes() else { return }; let value_offset = value_offset.start(); let Ok(value) = value.value() else { return }; // FIXME: Handle source maps for escaped text. - self.extend_with_doc_str(&value, value_offset, indent); + self.extend_with_doc_str(&value, value_offset, DocCommentKind::Desugared, indent); } - pub(crate) fn extend_with_doc_str( + fn extend_with_doc_str( &mut self, doc: &str, offset_in_ast: TextSize, - indent: &mut usize, + comment_kind: DocCommentKind, + indent: &mut Indent, ) { - self.push_doc_lines(doc, Some(offset_in_ast), indent); + self.push_doc_lines(doc, Some(offset_in_ast), comment_kind, indent); } - fn extend_with_unmapped_doc_str(&mut self, doc: &str, indent: &mut usize) { - self.push_doc_lines(doc, None, indent); + fn extend_with_unmapped_doc_str(&mut self, doc: &str, indent: &mut Indent) { + self.push_doc_lines(doc, None, DocCommentKind::Desugared, indent); } - fn push_doc_lines(&mut self, doc: &str, mut ast_offset: Option, indent: &mut usize) { - for line in doc.split('\n') { - self.docs_source_map - .push(DocsSourceMapLine { string_offset: TextSize::of(&self.docs), ast_offset }); - if let Some(ref mut offset) = ast_offset { - *offset += TextSize::of(line) + TextSize::of("\n"); + /// Beautifies `doc` and appends the result to `self.docs`, one line at a time via + /// [`Docs::push_doc_line`]. Mirrors rustc's [`beautify_doc_string`], delegating to + /// [`get_vertical_trim`] and [`get_horizontal_trim`] for the multi-line case. + /// + /// Individual `///` line comments always reach us as a single-line `doc`, so the + /// `!doc.contains('\n')` fast path fires and the multi-line logic never runs on them. + /// Desugared `#[doc = "..."]` strings and macro-expanded docs also route through here + /// with `shape = CommentShape::Line`, matching rustc. + /// + /// Unlike rustc's version, which joins the beautified lines into a new interned `Symbol`, + /// this port pushes each line individually via [`Docs::push_doc_line`] and pairs it with + /// its byte offset relative to `doc`'s start so the source-map records accurate per-line + /// offsets. + /// + /// [`beautify_doc_string`]: https://github.com/rust-lang/rust/blob/16a623ad672a92409b5c04beb303583c6cf72a7e/compiler/rustc_ast/src/util/comments.rs#L37 + fn push_doc_lines( + &mut self, + doc: &str, + ast_offset: Option, + comment_kind: DocCommentKind, + indent: &mut Indent, + ) { + // Note: this is pushed even if there are only empty lines here, because that's what rustdoc does. + let shape = match comment_kind { + DocCommentKind::Sugared(shape) => { + indent.seen_sugared = true; + shape } + // rustc uses `Line` for desugared comments. + DocCommentKind::Desugared => ast::CommentShape::Line, + }; + + if !doc.contains('\n') { + self.push_doc_line(doc, ast_offset, comment_kind, indent); + return; + } - let line = line.trim_end(); - if let Some(line_indent) = line.chars().position(|ch| !ch.is_whitespace()) { - // Empty lines are handled because `position()` returns `None` for them. - *indent = std::cmp::min(*indent, line_indent); + let mut lines: Vec<(&str, TextSize)> = doc + .lines() + .map(|line| { + let offset = TextSize::new(doc.substr_range(line).unwrap().start as u32); + (line, offset) + }) + .collect(); + + let raw_lines: Vec<&str> = lines.iter().map(|(l, _)| *l).collect(); + let lines = match get_vertical_trim(&raw_lines) { + Some((i, j)) => &mut lines[i..j], + None => &mut lines[..], + }; + + let raw_lines: Vec<&str> = lines.iter().map(|(l, _)| *l).collect(); + if let Some(horizontal) = get_horizontal_trim(&raw_lines, shape) { + let horizontal_len = TextSize::of(horizontal.as_str()); + // Strip `"[ \t]*\*"` from each line where present, exactly like rustc. + for (line, line_offset) in lines.iter_mut() { + if let Some(rest) = line.strip_prefix(horizontal.as_str()) { + *line = rest; + *line_offset += horizontal_len; + if shape == ast::CommentShape::Block + && (*line == "*" || line.starts_with("* ") || line.starts_with("**")) + { + *line = &line[1..]; + *line_offset += TextSize::of("*"); + } + } } - self.docs.push_str(line); - self.docs.push('\n'); } + + for (line, line_offset) in lines.iter().copied() { + self.push_doc_line(line, ast_offset.map(|it| it + line_offset), comment_kind, indent); + } + } + + /// Appends a single beautified line to `self.docs` and records its source-map row. + fn push_doc_line( + &mut self, + line: &str, + ast_offset: Option, + comment_kind: DocCommentKind, + indent: &mut Indent, + ) { + self.docs_source_map + .push(DocsSourceMapLine { string_offset: TextSize::of(&self.docs), ast_offset }); + + let line = line.trim_end(); + let line_indent = if line.chars().any(|ch| !ch.is_whitespace()) { + // Empty lines are handled because `any()` returns `false` for them. + let line_indent = line.bytes().take_while(|c| *c == b' ' || *c == b'\t').count(); + Some((line_indent, comment_kind)) + } else { + None + }; + indent.lines.push(line_indent); + self.docs.push_str(line); + self.docs.push('\n'); } - fn remove_indent(&mut self, indent: usize, start_source_map_index: usize) { + fn remove_indent(&mut self, indent: &Indent) { /// In case of panics, we want to avoid corrupted UTF-8 in `self.docs`, so we clear it. struct Guard<'a>(&'a mut Docs); impl Drop for Guard<'_> { @@ -271,8 +372,38 @@ impl Docs { return; } + // `add` is used in case the most common sugared doc syntax is used ("/// "). The other + // fragments kind's lines are never starting with a whitespace unless they are using some + // markdown formatting requiring it. Therefore, if the doc block have a mix between the two, + // we need to take into account the fact that the minimum indent minus one (to take this + // whitespace into account). + // + // For example: + // + // /// hello! + // #[doc = "another"] + // + // In this case, you want "hello! another" and not "hello! another". + let add_indent = if indent.seen_sugared { 1 } else { 0 }; + + let Some(min_indent) = indent + .lines + .iter() + .filter_map(|it| *it) + .map(|(line_indent, line_kind)| { + line_indent + + match line_kind { + DocCommentKind::Sugared(_) => 0, + DocCommentKind::Desugared => add_indent, + } + }) + .min() + else { + return; + }; + let guard = Guard(self); - let source_map = &mut guard.0.docs_source_map[start_source_map_index..]; + let source_map = guard.0.docs_source_map.as_mut_slice(); let Some(&DocsSourceMapLine { string_offset: mut copy_into, .. }) = source_map.first() else { return; @@ -289,7 +420,14 @@ impl Docs { let line_docs = &guard.0.docs[TextRange::new(line_source.string_offset, string_end_offset)]; let line_docs_len = TextSize::of(line_docs); - let indent_size = line_docs.char_indices().nth(indent).map_or_else( + let indent_size = if let Some((_, DocCommentKind::Desugared)) = indent.lines[idx] + && min_indent > 0 + { + min_indent - add_indent + } else { + min_indent + }; + let indent_size = line_docs.char_indices().nth(indent_size).map_or_else( || TextSize::of(line_docs) - TextSize::of("\n"), |(offset, _)| TextSize::new(offset as u32), ); @@ -347,6 +485,79 @@ impl Docs { } } +/// Copied verbatim from rustc's [`beautify_doc_string`], modulo `CommentKind`/`CommentShape` +/// renaming. +/// +/// [`beautify_doc_string`]: https://github.com/rust-lang/rust/blob/16a623ad672a92409b5c04beb303583c6cf72a7e/compiler/rustc_ast/src/util/comments.rs#L38 +fn get_vertical_trim(lines: &[&str]) -> Option<(usize, usize)> { + let mut i = 0; + let mut j = lines.len(); + // first line of all-stars should be omitted + if lines.first().is_some_and(|line| line.chars().all(|c| c == '*')) { + i += 1; + } + + // like the first, a last line of all stars should be omitted + if j > i && !lines[j - 1].is_empty() && lines[j - 1].chars().all(|c| c == '*') { + j -= 1; + } + + if i != 0 || j != lines.len() { Some((i, j)) } else { None } +} + +/// Copied verbatim from rustc's [`beautify_doc_string`], modulo `CommentKind`/`CommentShape` +/// renaming and returning `String` rather than interning to `Symbol`. +/// +/// [`beautify_doc_string`]: https://github.com/rust-lang/rust/blob/16a623ad672a92409b5c04beb303583c6cf72a7e/compiler/rustc_ast/src/util/comments.rs#L54 +fn get_horizontal_trim(lines: &[&str], kind: ast::CommentShape) -> Option { + let mut i = usize::MAX; + let mut first = true; + + // In case we have doc comments like `/**` or `/*!`, we want to remove stars if they are + // present. However, we first need to strip the empty lines so they don't get in the middle + // when we try to compute the "horizontal trim". + let lines = match kind { + ast::CommentShape::Block => { + // Whatever happens, we skip the first line. + let mut i = lines + .first() + .map(|l| if l.trim_start().starts_with('*') { 0 } else { 1 }) + .unwrap_or(0); + let mut j = lines.len(); + + while i < j && lines[i].trim().is_empty() { + i += 1; + } + while j > i && lines[j - 1].trim().is_empty() { + j -= 1; + } + &lines[i..j] + } + ast::CommentShape::Line => lines, + }; + + for line in lines { + for (j, c) in line.chars().enumerate() { + if j > i || !"* \t".contains(c) { + return None; + } + if c == '*' { + if first { + i = j; + first = false; + } else if i != j { + return None; + } + break; + } + } + if i >= line.len() { + return None; + } + } + Some(lines.first()?[..i].to_string()) +} + struct DocMacroExpander<'db> { db: &'db dyn SourceDatabase, krate: Crate, @@ -442,7 +653,7 @@ fn extend_with_attrs<'a, 'db>( node: &SyntaxNode, file_id: HirFileId, expect_inner_attrs: bool, - indent: &mut usize, + indent: &mut Indent, get_cfg_options: &dyn Fn() -> &'a CfgOptions, cfg_options: &mut Option<&'a CfgOptions>, make_resolver: &dyn Fn() -> Resolver<'db>, @@ -525,8 +736,8 @@ pub(crate) fn extract_docs<'a, 'db>( let mut cfg_options = None; + let mut indent = Indent::default(); if let Some(outer_mod_decl) = outer_mod_decl { - let mut indent = usize::MAX; // For outer docs (the `mod foo;` declaration), use the module's own resolver. extend_with_attrs( &mut result, @@ -540,12 +751,9 @@ pub(crate) fn extract_docs<'a, 'db>( &mut cfg_options, resolver, ); - result.remove_indent(indent, 0); result.outline_mod = Some((outer_mod_decl.file_id, result.docs_source_map.len())); } - let inline_source_map_start = result.docs_source_map.len(); - let mut indent = usize::MAX; // For inline docs, use the item's own resolver. extend_with_attrs( &mut result, @@ -574,7 +782,7 @@ pub(crate) fn extract_docs<'a, 'db>( resolver, ); } - result.remove_indent(indent, inline_source_map_start); + result.remove_indent(&indent); result.remove_last_newline(); @@ -587,13 +795,14 @@ pub(crate) fn extract_docs<'a, 'db>( mod tests { use expect_test::expect; use hir_expand::InFile; + use syntax::{AstToken, ast}; use test_fixture::WithFixture; use thin_vec::ThinVec; use tt::{TextRange, TextSize}; use crate::test_db::TestDB; - use super::{Docs, IsInnerDoc}; + use super::{DocCommentKind, Docs, Indent, IsInnerDoc}; #[test] fn docs() { @@ -608,12 +817,17 @@ mod tests { outline_inner_docs_start: None, macro_calls: ThinVec::new(), }; - let mut indent = usize::MAX; + let mut indent = Indent::default(); let outer = " foo\n\tbar baz"; let mut ast_offset = TextSize::new(123); for line in outer.split('\n') { - docs.extend_with_doc_str(line, ast_offset, &mut indent); + docs.extend_with_doc_str( + line, + ast_offset, + DocCommentKind::Sugared(ast::CommentShape::Line), + &mut indent, + ); ast_offset += TextSize::of(line) + TextSize::of("\n"); } @@ -621,11 +835,15 @@ mod tests { ast_offset += TextSize::new(123); let inner = " bar \n baz"; for line in inner.split('\n') { - docs.extend_with_doc_str(line, ast_offset, &mut indent); + docs.extend_with_doc_str( + line, + ast_offset, + DocCommentKind::Sugared(ast::CommentShape::Line), + &mut indent, + ); ast_offset += TextSize::of(line) + TextSize::of("\n"); } - assert_eq!(indent, 1); expect![[r#" [ DocsSourceMapLine { @@ -656,7 +874,7 @@ mod tests { "#]] .assert_debug_eq(&docs.docs_source_map); - docs.remove_indent(indent, 0); + docs.remove_indent(&indent); assert_eq!(docs.inline_inner_docs_start, Some(TextSize::new(13))); @@ -762,4 +980,107 @@ mod tests { Some((in_file(range(263, 265)), IsInnerDoc::Yes)) ); } + + #[test] + fn sugared_desugared_mix() { + let (_db, file_id) = TestDB::with_single_file(""); + let mut docs = Docs { + docs: String::new(), + docs_source_map: Vec::new(), + outline_mod: None, + inline_file: file_id.into(), + prefix_len: TextSize::new(0), + inline_inner_docs_start: None, + outline_inner_docs_start: None, + macro_calls: ThinVec::new(), + }; + let mut indent = Indent::default(); + + docs.push_doc_lines( + " hello!", + None, + DocCommentKind::Sugared(ast::CommentShape::Line), + &mut indent, + ); + docs.push_doc_lines("another", None, DocCommentKind::Desugared, &mut indent); + docs.remove_indent(&indent); + docs.remove_last_newline(); + + assert_eq!(docs.docs(), "hello!\nanother"); + } + + /// Extracts the docs of the first comment in `source`, running the same normalization as + /// [`super::extract_docs`] does for inline docs. + fn comment_docs(source: &str) -> Docs { + let (_db, file_id) = TestDB::with_single_file(""); + let comment = syntax::SourceFile::parse(source, span::Edition::CURRENT) + .syntax_node() + .descendants_with_tokens() + .filter_map(|it| it.into_token()) + .find_map(ast::Comment::cast) + .expect("no comment in the fixture"); + let mut docs = Docs { + docs: String::new(), + docs_source_map: Vec::new(), + outline_mod: None, + inline_file: file_id.into(), + prefix_len: TextSize::new(0), + inline_inner_docs_start: None, + outline_inner_docs_start: None, + macro_calls: ThinVec::new(), + }; + let mut indent = Indent::default(); + docs.extend_with_doc_comment(comment, &mut indent); + docs.remove_indent(&indent); + docs.remove_last_newline(); + docs + } + + #[test] + fn block_doc_comment_stars() { + #[track_caller] + fn check(source: &str, expect: expect_test::Expect) { + expect.assert_eq(&comment_docs(source).docs); + } + + // The decoration is stripped, but markdown bullets and `*foo` are content. + // `*bar` doesn't start with `* ` / `**`, so rustc's beautifier only strips the + // horizontal `[ \t]*` prefix (here a single space) and leaves the leading `*` in + // place. That in turn pins the block's minimum indent at 0, so surrounding lines + // aren't re-indented. + check( + "/**\n * foo\n *\n * * bullet\n *bar\n */", + expect![[r#" + foo + + * bullet + *bar + "#]], + ); + // Single-line block doc comments are left alone, like rustdoc does. + check("/** * item */", expect!["* item"]); + // So are blocks without a consistent star column. + check( + "/**\n * foo\n * bar\n */", + expect![[r#" + * foo + * bar + "#]], + ); + } + + #[test] + fn block_doc_comment_source_map() { + let docs = comment_docs("/**\n * foo\n * bar\n */"); + // `.lines()` (matching rustc) doesn't emit a leading empty entry for the newline + // right after `/**`, so the docs body starts at `foo`, not with a blank line. + assert_eq!(docs.docs, "foo\nbar\n"); + + let range = |start, end| TextRange::new(TextSize::new(start), TextSize::new(end)); + let in_file = |range| InFile::new(docs.inline_file, range); + let mapped = |start, end| docs.find_ast_range(range(start, end)); + // Both `foo` and `bar` map back past the stripped ` * ` decoration. + assert_eq!(mapped(0, 3), Some((in_file(range(7, 10)), IsInnerDoc::No))); + assert_eq!(mapped(4, 7), Some((in_file(range(14, 17)), IsInnerDoc::No))); + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 91faafaf843e0..08cecb52570eb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -135,7 +135,9 @@ pub(super) fn lower_body( } collector.with_expr_root(|collector| { - if let Some(param_list) = parameters { + if let DefWithBodyId::FunctionId(func) = owner + && let Some(param_list) = parameters + { if let Some(self_param_syn) = param_list.self_param().filter(|it| collector.check_cfg(it)) { @@ -155,23 +157,28 @@ pub(super) fn lower_body( Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); } - let is_extern = matches!( - owner, - DefWithBodyId::FunctionId(id) - if matches!(id.loc(db).container, ItemContainerId::ExternBlockId(_)), - ); + let params_are_bare_idents = match func.loc(db).container { + ItemContainerId::ExternBlockId(_) => true, + ItemContainerId::TraitId(_) => body.is_none(), + ItemContainerId::ModuleId(_) | ItemContainerId::ImplId(_) => false, + }; for param in param_list.params() { if collector.check_cfg(¶m) { - let param_pat = if is_extern { - collector.collect_extern_fn_param(param.pat()) - } else { - collector.collect_pat_top(param.pat()) + let param_pat = match param.pat() { + Some(pat) => { + if params_are_bare_idents { + collector.collect_param_as_ident(pat) + } else { + collector.collect_pat_top(Some(pat)) + } + } + None => collector.missing_pat(), }; params.push(Param::new(param_pat)); } } - }; + } collector.collect( &mut self_param, @@ -2792,11 +2799,11 @@ impl<'db> ExprCollector<'db> { } } - fn collect_extern_fn_param(&mut self, pat: Option) -> PatId { - // parameters of functions in `extern` blocks can only be simple identifiers and wildcards. + fn collect_param_as_ident(&mut self, pat: ast::Pat) -> PatId { + // parameters of functions in `extern` blocks and associated trait functions without a body + // can only be simple identifiers and wildcards. // Furthermore, the identifiers in their parameters are always interpreted as bindings, even // if in a normal function they won't be, because they would refer to a path pattern. - let Some(pat) = pat else { return self.missing_pat() }; match &pat { ast::Pat::IdentPat(bp) if bp.is_simple_ident() => { @@ -2812,6 +2819,8 @@ impl<'db> ExprCollector<'db> { pat } ast::Pat::WildcardPat(_) => self.alloc_pat(Pat::Wild, AstPtr::new(&pat)), + ast::Pat::MacroPat(mac) => self + .collect_macro_pat_with(mac.clone(), |this, pat| this.collect_param_as_ident(pat)), _ => { self.store.diagnostics.push(ExpressionStoreDiagnostics::PatternArgInExternFn { node: self.expander.in_file(AstPtr::new(&pat)), @@ -3017,19 +3026,11 @@ impl<'db> ExprCollector<'db> { Pat::Missing } } - ast::Pat::MacroPat(mac) => match mac.macro_call() { - Some(call) => { - let macro_ptr = AstPtr::new(&call); - let src = self.expander.in_file(AstPtr::new(&pat)); - let pat = - self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| { - this.collect_pat_opt(expanded_pat, binding_list) - }); - self.store.pat_map.insert(src, pat.into()); - return pat; - } - None => Pat::Missing, - }, + ast::Pat::MacroPat(mac) => { + return self.collect_macro_pat_with(mac.clone(), |this, expanded_pat| { + this.collect_pat(expanded_pat, binding_list) + }); + } ast::Pat::RangePat(p) => { let mut range_part_lower = |p: Option| -> Option { p.and_then(|it| { @@ -3068,6 +3069,28 @@ impl<'db> ExprCollector<'db> { self.alloc_pat(pattern, ptr) } + fn collect_macro_pat_with( + &mut self, + mac: ast::MacroPat, + callback: impl FnOnce(&mut Self, ast::Pat) -> PatId, + ) -> PatId { + match mac.macro_call() { + Some(call) => { + let macro_ptr = AstPtr::new(&call); + let src = self.expander.in_file(AstPtr::new(&mac.into())); + let pat = self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| { + match expanded_pat { + Some(pat) => callback(this, pat), + None => this.missing_pat(), + } + }); + self.store.pat_map.insert(src, pat.into()); + pat + } + None => self.missing_pat(), + } + } + fn collect_pat_opt(&mut self, pat: Option, binding_list: &mut BindingList) -> PatId { match pat { Some(pat) => self.collect_pat(pat, binding_list), @@ -3172,9 +3195,7 @@ impl<'db> ExprCollector<'db> { ) } ast::Pat::MacroPat(pat) => { - let Some(call) = pat.macro_call() else { return self.missing_pat() }; - let ptr = AstPtr::new(&call); - self.collect_macro_call(call, ptr, true, |this, pat| this.collect_ty_pat_opt(pat)) + self.collect_macro_pat_with(pat, |this, pat| this.collect_ty_pat(pat)) } _ => { // FIXME: Emit an error. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 5b11f5ff8bbb6..9d5d80a73ef6a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -1392,7 +1392,9 @@ impl HasResolver for FunctionId { impl HasResolver for ConstId { fn resolver(self, db: &dyn SourceDatabase) -> Resolver<'_> { - lookup_resolver(db, self) + // Consts can have generic params on nightly. Furthermore they're a `GenericDefId`, + // so not pushing a generic params scope here complicates things (e.g. `TypeOwnerId` tracking). + lookup_resolver(db, self).push_generic_params_scope(db, self.into()) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 3021de68f3fdf..58598980707b3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -315,9 +315,7 @@ impl<'db> UnsafeVisitor<'db> { // https://github.com/rust-lang/rust/pull/129248 // Taking a raw ref to a deref place expr is always safe. Expr::UnaryOp { expr, op: UnaryOp::Deref } => { - self.body - .walk_child_exprs_without_pats(expr, |child| self.walk_expr(child)); - + self.walk_expr(expr); return; } _ => (), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 9bb0e5b66beeb..dfa088830543f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1709,11 +1709,13 @@ impl<'db> HirDisplay<'db> for Ty<'db> { write!(f, "?c.{}", ty.var.as_usize())? } TyKind::Dynamic(bounds, region) => { + let self_ty = interner.default_types().types.dyn_trait_dummy_self; + // We want to put auto traits after principal traits, regardless of their written order. let mut bounds_to_display = SmallVec::<[_; 4]>::new(); let mut auto_trait_bounds = SmallVec::<[_; 4]>::new(); for bound in bounds.iter() { - let clause = bound.with_self_ty(interner, *self); + let clause = bound.with_self_ty(interner, self_ty); match bound.skip_binder() { ExistentialPredicate::Trait(_) | ExistentialPredicate::Projection(_) => { bounds_to_display.push(clause); @@ -1725,13 +1727,13 @@ impl<'db> HirDisplay<'db> for Ty<'db> { if f.render_region(region) { bounds_to_display - .push(rustc_type_ir::OutlivesPredicate(*self, region).upcast(interner)); + .push(rustc_type_ir::OutlivesPredicate(self_ty, region).upcast(interner)); } write_bounds_like_dyn_trait_with_prefix( f, "dyn", - Either::Left(*self), + Either::Left(self_ty), &bounds_to_display, SizedByDefault::NotSized, trait_bounds_need_parens, @@ -1974,12 +1976,12 @@ impl<'db> HirDisplay<'db> for PolyFnSig<'db> { if let Safety::Unsafe = fn_sig_kind.safety() { write!(f, "unsafe ")?; } - // FIXME: Enable this when the FIXME on FnAbi regarding PartialEq is fixed. - // if !matches!(abi, FnAbi::Rust) { - // f.write_str("extern \"")?; - // f.write_str(abi.as_str())?; - // f.write_str("\" ")?; - // } + let abi = self.abi(); + if !matches!(abi, ExternAbi::Rust) { + f.write_str("extern \"")?; + f.write_str(abi.as_str())?; + f.write_str("\" ")?; + } write!(f, "fn(")?; f.write_joined(inputs_and_output.inputs(), ", ")?; if fn_sig_kind.c_variadic() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 70539cf83673a..3fbb02aee94bd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1629,10 +1629,6 @@ impl<'db> InferenceContext<'db> { self.defined_anon_consts.borrow_mut().append(&mut defined_anon_consts); } - // FIXME: This function should be private in module. It is currently only used in the consteval, since we need - // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you - // used this function for another workaround, mention it here. If you really need this function and believe that - // there is no problem in it being `pub(crate)`, remove this comment. fn resolve_all(self) -> InferenceResult<'db> { let InferenceContext { table, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index f247b517c541f..fc53d64a2f984 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1329,7 +1329,11 @@ impl<'db> InferenceContext<'db> { } None => { let ty = self.table.next_ty_var(element.into()); - self.infer_expr(element, &Expectation::has_type(ty), ExprIsRead::Yes); + self.infer_expr_suptype_coerce_never( + element, + &Expectation::has_type(ty), + ExprIsRead::Yes, + ); ty } }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 5f7e6782fdd28..7733b49d32f78 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -788,7 +788,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ) } - /// This is only for `generic_predicates_for_param`, where we can't just + /// This is only for [`resolve_type_param_assoc_type_shorthand`], where we can't just /// lower the self types of the predicates since that could lead to cycles. /// So we just check here if the `type_ref` resolves to a generic param, and which. fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index bd1ad70fe6a14..44f410408a40a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -491,6 +491,12 @@ impl<'db> MirLowerCtx<'_, 'db> { )? } Pat::Ref { pat, mutability: _ } => { + let ty = cond_place.ty(&self.result, &self.infcx, self.env).ty; + if !ty.is_ref() { + return Err(MirLowerError::TypeError( + "non reference type matched with reference pattern", + )); + } let cond_place = cond_place.project(ProjectionElem::Deref); self.pattern_match_inner(current, current_else, cond_place, *pat, mode)? } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs index 1f6aa5c926b15..e4f3b61be4556 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs @@ -49,3 +49,19 @@ fn foo() { "#, ); } + +#[test] +fn ref_pattern_on_unresolved_alias() { + lower_mir( + r#" +//- minicore: sized +trait Tr { + type A; +} + +fn f(x: T::A) { + let &() = x; +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index fe7327134903e..efbb49b0eeb81 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs @@ -90,6 +90,19 @@ fn foo(foo: &dyn for<'a> Foo<'a>) {} ); } +#[test] +fn render_dyn_ty_under_enclosing_binder() { + check_types_source_code( + r#" +//- minicore: fn +fn test(f: impl for<'b> Fn(&dyn Fn() -> &'b u8)) { + f; + //^ impl Fn(&(dyn Fn() -> &u8 + 'static)) +} +"#, + ); +} + #[test] fn sized_bounds_apit() { check_types_source_code( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 1c5f8aa110463..e53f7503dac6b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -114,6 +114,21 @@ fn test() { ); } +#[test] +fn array_repeat_never_can_be_reinferred() { + check_no_mismatches( + r#" +fn test() { + let y = [return; 2]; + match y { + [(1, _), (_, false)] => {} + [_, _] => {} + } +} +"#, + ); +} + #[test] fn match_no_arm() { check_types( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 3ed2eb2594c56..ff0e075ff6d69 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2797,10 +2797,48 @@ where fn extern_fns_cannot_have_param_patterns() { check_no_mismatches( r#" -pub(crate) struct Builder<'a>(&'a ()); +macro_rules! m { + () => { Builder }; +} + +pub(crate) struct Builder; + +unsafe extern "C" { + pub(crate) fn foo(Builder: (), m!(): ()); +} + "#, + ); +} + +#[test] +fn trait_assoc_fns_cannot_have_param_patterns() { + check_no_mismatches( + r#" +macro_rules! m { + () => { Builder }; +} -unsafe extern "C" { - pub(crate) fn foo<'a>(Builder: &Builder<'a>); +pub(crate) struct Builder; + +trait Trait { + fn foo(Builder: (), m!(): ()); +} + "#, + ); + // But assoc fns with bodies do have patterns: + check( + r#" +macro_rules! m { + () => { Builder }; +} + +pub(crate) struct Builder; + +trait Trait { + fn foo(Builder: (), + // ^^^^^^^ expected (), got Builder + m!(): ()) {} + // ^^ expected (), got Builder } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 8f747e397c873..9238cdcb3ef85 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3956,21 +3956,9 @@ impl<'db> GenericSubstitution<'db> { TypeOrConstParamData::TypeParamData(param) => Some(param.name.clone()), TypeOrConstParamData::ConstParamData(_) => None, }); - let parent_len = self.subst.len() - - generics - .iter_type_or_consts() - .filter(|g| matches!(g.1, TypeOrConstParamData::TypeParamData(..))) - .count(); - let container_params = self.subst.as_slice()[..parent_len] - .iter() - .filter_map(|param| param.ty()) - .zip(container_type_params.into_iter().flatten()); - let self_params = self.subst.as_slice()[parent_len..] - .iter() - .filter_map(|param| param.ty()) - .zip(type_params); - container_params - .chain(self_params) + self.subst + .types() + .zip(container_type_params.into_iter().flatten().chain(type_params)) .filter_map(|(ty, name)| { Some(( name?.symbol().clone(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs index e7203a96bb218..453a1b26e08fd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs @@ -161,7 +161,7 @@ fn extract_tail(ctx: &AssistContext<'_, '_>) -> Option<(FnType, ast::Expr, Inser let stmt_list = body.stmt_list()?; let tail_expr = stmt_list.tail_expr()?; - let ret_range_end = stmt_list.l_curly_token()?.text_range().start(); + let ret_range_end = stmt_list.l_curly_token()?.text_range().end(); let ret_range = TextRange::new(rparen_pos, ret_range_end); (FnType::Function, tail_expr, ret_range, action) } @@ -215,7 +215,7 @@ mod tests { #[test] fn infer_return_type_cursor_at_return_type_pos() { - cov_mark::check!(cursor_in_ret_position); + cov_mark::check_count!(cursor_in_ret_position, 3); check_assist( add_return_type, r#"fn foo() $0{ @@ -223,6 +223,24 @@ mod tests { }"#, r#"fn foo() -> i32 { 45 +}"#, + ); + check_assist( + add_return_type, + r#"fn foo()$0 { + 45 +}"#, + r#"fn foo() -> i32 { + 45 +}"#, + ); + check_assist( + add_return_type, + r#"fn foo() {$0 + 45 +}"#, + r#"fn foo() -> i32 { + 45 }"#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index c2c50b16de76f..e514e8be4233a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -76,10 +76,16 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - if let Some(t) = ctx.token_at_offset().find(|it| it.kind() == T![;]) { t.parent().and_then(ast::ExprStmt::cast)?.syntax().clone() } else { - let expr = ancestors_at_offset(ctx.source_file().syntax(), ctx.offset()) - .next() - .and_then(ast::Expr::cast)?; - expr.syntax().ancestors().find_map(valid_target_expr(ctx))?.syntax().clone() + // Offer the assist only if the nearest syntax node is an expression, or a record + // field, or a record field’s name. This prevents the assist from appearing when + // it is unlikely to be relevant, such as when the cursor is in a pattern. + // (If we did not want to restrict it this way, we could just apply + // `valid_target_expr()` to all ancestors.) + let expr_or_field = ancestors_at_offset(ctx.source_file().syntax(), ctx.offset()) + .find(|it| !ast::NameRef::can_cast(it.kind())) + .and_then(either::Either::::cast)?; + + expr_or_field.syntax().ancestors().find_map(valid_target_expr(ctx))?.syntax().clone() } } else { match ctx.covering_element() { @@ -95,7 +101,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - let node = node.ancestors().take_while(|anc| anc.text_range() == node.text_range()).last()?; let range = node.text_range(); - let (to_replace, analysis, use_source_expr) = if node.kind() == SyntaxKind::TOKEN_TREE { + let (to_replace, analysis, source_to_extract) = if node.kind() == SyntaxKind::TOKEN_TREE { let (first, last) = extract_token_range_of(&node, ctx.selection_trimmed())?; let first_descend = ctx.sema.descend_into_macros_single_exact(first.clone()); @@ -114,14 +120,16 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - if !node.text_range().contains_range(original_range.range) { return None; } - (cover_edit_range(&node, original_range.range), expr, true) + let to_replace = cover_edit_range(&node, original_range.range); + let source_to_extract = source_expr(ctx, to_replace.clone())?; + (to_replace, expr, Some(source_to_extract)) } else { let expr = node .descendants() .take_while(|it| range.contains_range(it.text_range())) .find_map(valid_target_expr(ctx))?; let to_extract = expr.syntax().syntax_element(); - (to_extract.clone()..=to_extract, expr, false) + (to_extract.clone()..=to_extract, expr, None) }; let place = match to_replace.start() { NodeOrToken::Node(node) => node.clone(), @@ -220,10 +228,9 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - editor.add_annotation(pat_name.syntax().clone(), tabstop); } - let to_extract_no_ref = if use_source_expr { - source_expr(ctx, to_replace.clone()).unwrap() - } else { - to_extract_no_ref.clone() + let to_extract_no_ref = match &source_to_extract { + Some(expr) => expr.clone(), + None => to_extract_no_ref.clone(), }; let initializer = match ty.as_ref().filter(|_| needs_ref) { Some(receiver_type) if receiver_type.is_mutable_reference() => { @@ -367,6 +374,11 @@ fn valid_target_expr(ctx: &AssistContext<'_, '_>) -> impl Fn(SyntaxNode) -> Opti let path_resolution = ctx.sema.resolve_path(&path_expr.path()?)?; like_const_value(ctx, path_resolution).then_some(path_expr.into()) } + SyntaxKind::RECORD_EXPR_FIELD => { + // If we are on `k` in `Struct { k: v }`, then extract `v`. + let record_field = ast::RecordExprField::cast(node)?; + record_field.expr() + } _ => ast::Expr::cast(node), } } @@ -952,6 +964,16 @@ fn foo() { check_assist_not_applicable(extract_variable, r#"fn main() { 1 + /* $0comment$0 */ 1; }"#); } + #[test] + fn dont_extract_in_pattern_with_selection() { + check_assist_not_applicable(extract_variable, r#"fn foo() { [].map(|$0bar$0| bar + 1) } "#); + } + + #[test] + fn dont_extract_in_pattern_without_selection() { + check_assist_not_applicable(extract_variable, r#"fn foo() { [].map(|b$0ar| bar + 1) } "#); + } + #[test] fn extract_var_expr_stmt() { cov_mark::check!(test_extract_var_expr_stmt); @@ -1383,6 +1405,46 @@ fn main() { ); } + #[test] + fn extract_var_in_macro_call_with_multiple_args() { + check_assist_not_applicable( + extract_variable, + r#" +macro_rules! m { + ($a:expr, $b:expr) => { $a + $b }; +} +fn f(x: u32) -> u32 { + m!($0x$0, 1) +} +"#, + ); + } + + #[test] + fn extract_var_in_macro_call_with_single_arg() { + check_assist_by_label( + extract_variable, + r#" +macro_rules! m { + ($e:expr) => { $e + 1 }; +} +fn f(x: u32) -> u32 { + m!($0x$0) +} +"#, + r#" +macro_rules! m { + ($e:expr) => { $e + 1 }; +} +fn f(x: u32) -> u32 { + let $0var_name = x; + m!(var_name) +} +"#, + "Extract into variable", + ); + } + #[test] fn extract_var_path_simple() { check_assist_by_label( @@ -1586,6 +1648,87 @@ struct S { foo: i32 } +fn main() { + let $0foo = 1 + 1; + S { foo } +} +"#, + "Extract into variable", + ) + } + + #[test] + fn extract_var_from_record_field() { + check_assist_by_label( + extract_variable, + r#" +struct S { + foo: i32 +} + +fn main() { + S { $0foo: 1 + 1,$0 } +} +"#, + r#" +struct S { + foo: i32 +} + +fn main() { + let $0foo = 1 + 1; + S { foo, } +} +"#, + "Extract into variable", + ) + } + + #[test] + fn extract_var_from_record_field_name() { + check_assist_by_label( + extract_variable, + r#" +struct S { + foo: i32 +} + +fn main() { + S { f$0oo: 1 + 1 } +} +"#, + r#" +struct S { + foo: i32 +} + +fn main() { + let $0foo = 1 + 1; + S { foo } +} +"#, + "Extract into variable", + ) + } + + #[test] + fn extract_var_from_record_field_colon() { + check_assist_by_label( + extract_variable, + r#" +struct S { + foo: i32 +} + +fn main() { + S { foo $0: 1 + 1 } +} +"#, + r#" +struct S { + foo: i32 +} + fn main() { let $0foo = 1 + 1; S { foo } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index f1a34f15d0a5b..eec35cc4024e5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -752,7 +752,7 @@ pub(super) fn complete_name_ref<'db>( TypeLocation::TypeAscription(ascription) => { if let TypeAscriptionTarget::RetType { item: Some(item), .. } = ascription - && path_ctx.required_thin_arrow().is_some() + && path_ctx.required_thin_arrow(&ctx.sema).is_some() && matches!(path_ctx.qualified, Qualified::No) { keyword::complete_for_and_where(acc, ctx, &item.clone().into()); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 705305f557e9c..5935e16542e4a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -103,7 +103,10 @@ impl PathCompletionCtx<'_> { ) } - pub(crate) fn required_thin_arrow(&self) -> Option<(&'static str, TextSize)> { + pub(crate) fn required_thin_arrow( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option<(&'static str, TextSize)> { let PathKind::Type { location: TypeLocation::TypeAscription(TypeAscriptionTarget::RetType { @@ -117,10 +120,11 @@ impl PathCompletionCtx<'_> { if fn_item.ret_type().is_some_and(|it| it.thin_arrow_token().is_some()) { return None; } + let unmap = |node: &_| sema.original_range_opt(node).map(|it| it.range); let ret_type = fn_item.ret_type().and_then(|it| it.ty()); match (ret_type, fn_item.param_list()) { - (Some(ty), _) => Some(("-> ", ty.syntax().text_range().start())), - (None, Some(param)) => Some((" ->", param.syntax().text_range().end())), + (Some(ty), _) => Some(("-> ", unmap(ty.syntax())?.start())), + (None, Some(param)) => Some((" ->", unmap(param.syntax())?.end())), (None, None) => None, } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 43b2a53a7f7ea..de0a9a9174314 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -651,7 +651,7 @@ fn adds_ret_type_arrow( item: &mut Builder, insert_text: String, ) { - if let Some((arrow, at)) = path_ctx.required_thin_arrow() { + if let Some((arrow, at)) = path_ctx.required_thin_arrow(&ctx.sema) { let mut edit = TextEdit::builder(); edit.insert(at, arrow.to_owned()); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 0e558cf6a2b57..0a4057df974e2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -4208,3 +4208,38 @@ fn foo(t: T) { "#]], ); } + +#[test] +fn const_is_type_owner() { + check( + r#" +pub struct Boo; +pub struct A(Boo); +impl A { + const X: A = A(B$0); +} + "#, + expect![[r#" + sp Self A + st A A + st Boo Boo + st Boo Boo + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw self:: + kw true + kw unsafe + kw while + kw while let + ex A::X.0 + ex Boo + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index ad058901c0473..419b15ed868b3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -244,6 +244,40 @@ fn foo() -> foo::Num "#, ); + check_edit( + "u32", + r#" +macro_rules! identity { ($($t:tt)*) => {$($t)*}; } +identity! { + fn foo() u$0 +} +"#, + r#" +macro_rules! identity { ($($t:tt)*) => {$($t)*}; } +identity! { + fn foo() -> u32 +} +"#, + ); + + check_edit( + "Num", + r#" +macro_rules! identity { ($($t:tt)*) => {$($t)*}; } +mod foo { pub type Num = u32; } +identity! { + fn foo() foo::N$0 +} +"#, + r#" +macro_rules! identity { ($($t:tt)*) => {$($t)*}; } +mod foo { pub type Num = u32; } +identity! { + fn foo() -> foo::Num +} +"#, + ); + // no spaces, test edit order check_edit( "foo", diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index e1c2053289b96..87647a84cc7ce 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -230,7 +230,7 @@ fn foo(_x: isize) { } fn main() { let v: u64 = 5; let x = foo as extern "C" fn() -> isize; - //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `fn foo(isize)` as `fn() -> isize` + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `fn foo(isize)` as `extern "C" fn() -> isize` let y = v as extern "Rust" fn(isize) -> (isize, isize); //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `u64` as `fn(isize) -> (isize, isize)` y(x()); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 18859c0db1e60..4b55995a058df 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -1061,6 +1061,17 @@ fn foo() {} #[target_feature(enable = "avx2", enable = "fma")] fn bar() { foo(); +} + "#, + ); + } + + #[test] + fn raw_ref_deref_raw_ref_deref() { + check_diagnostics( + r#" +fn foo() { + &raw const *&raw const *&raw const *&2; } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 682a8130a8822..26f5e45ea3420 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -85,6 +85,7 @@ fn field_fix(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>) -> if !is_editable_crate(target_module.krate(ctx.sema.db), ctx.sema.db) || SyntaxKind::from_keyword(field_name, ctx.edition).is_some() + || !syntax::utils::is_identifier(field_name, ctx.edition) { return None; } @@ -148,11 +149,7 @@ fn add_field_to_struct_fix( Some(make::visibility_pub_crate()) }; - let field_name = match field_name.chars().next() { - Some(ch) if ch.is_numeric() => return None, - Some(_) => make::name(field_name), - None => return None, - }; + let field_name = make::name(field_name); let (offset, record_field) = record_field_layout( visibility, @@ -180,12 +177,7 @@ fn add_field_to_struct_fix( // Add a field list to the Unit Struct let mut src_change_builder = SourceChangeBuilder::new(struct_range.file_id.file_id(ctx.sema.db)); - let field_name = match field_name.chars().next() { - // FIXME : See match arm below regarding tuple structs. - Some(ch) if ch.is_numeric() => return None, - Some(_) => make::name(field_name), - None => return None, - }; + let field_name = make::name(field_name); let visibility = if error_range.file_id == struct_range.file_id { None } else { @@ -524,6 +516,18 @@ fn main() {} ) } + #[test] + fn no_fix_when_indexed_on_union() { + check_no_fix( + r#" +union U { a: u32 } +fn main(u: U) { + u.0$0; +} +"#, + ) + } + #[test] fn no_fix_when_without_field() { check_no_fix( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index fad322aa4f5da..66ccd1924639e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -2626,7 +2626,7 @@ fn bar() { fo$0o(); } --- - \<- ` ` here +  \<- ` ` here "#]], ); } @@ -5085,6 +5085,33 @@ fn foo$0() {} ); } +#[test] +fn hover_doc_block_style_leading_asterisks() { + check( + r#" +/** + * Some docs, *not a bullet*. + */ +fn foo$0() {} +"#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture + ``` + + ```rust + fn foo() + ``` + + --- + + Some docs, *not a bullet*. + "#]], + ); +} + #[test] fn hover_comments_dont_highlight_parent() { cov_mark::check!(no_highlight_on_comment_hover); @@ -11970,3 +11997,55 @@ fn main() { let _ = resolved.as_array(db); }); } + +#[test] +fn extern_c_fn_ptr_display() { + check( + r#" +extern "C" fn foo() {} + +fn bar() { + let v$0 = foo as extern "C" fn(); +} + "#, + expect![[r#" + *v* + + ```rust + let v: extern "C" fn() + ``` + + --- + + size = 8, align = 8, niches = 1, no Drop + "#]], + ); +} + +#[test] +fn subst_impl_trait_arg_with_const_generic() { + check( + r#" +fn main() { + generic$0_tn([()], 1); +} + +fn generic_tn(_: [T; N], _: impl Sized) {} +"#, + expect![[r#" + *generic_tn* + + ```rust + ra_test_fixture + ``` + + ```rust + fn generic_tn(_: [T; {const}], _: impl Sized) + ``` + + --- + + `T` = `()` + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index c95b36a1b4f33..ede25fc2050e8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -161,18 +161,18 @@ } /// ```rust -/// let _ = example(&[1, 2, 3]); +/// let _ = example(&[1, 2, 3]); /// ``` /// /// ``` -/// loop {} +/// loop {} #[cfg_attr(not(feature = "false"), doc = "loop {}")] #[doc = "loop {}"] /// ``` /// #[cfg_attr(feature = "alloc", doc = "```rust")] #[cfg_attr(not(feature = "alloc"), doc = "```ignore")] -/// let _ = example(&alloc::vec![1, 2, 3]); +/// let _ = example(&alloc::vec![1, 2, 3]); /// ``` pub fn mix_and_match() {} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs index f8be75b1787f0..fdc3f8c491eaf 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs @@ -66,6 +66,12 @@ fn pattern_r(p: &mut Parser<'_>, recovery_set: TokenSet) { fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) { // test range_pat // fn main() { + // match () { + // (..1) => (), + // (..=3) => (), + // (..2 | 4) => (), + // } + // // match 92 { // 0 ... 100 => (), // 101 ..= 200 => (), @@ -97,6 +103,7 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) { // (1.., _) => (), // (..=2, _) => (), // } + // // } if p.at(T![..=]) { @@ -484,8 +491,7 @@ fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker { p.error("expected a pattern"); break; } - has_rest |= p.at(T![..]); - + has_rest |= !p.at(T![..=]) && p.at(T![..]) && !RANGE_PAT_END_FIRST.contains(p.nth(2)); pattern(p); if !p.at(T![')']) { has_comma = true; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rast index d9981c50719f3..ba0198e9d542c 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rast @@ -12,6 +12,77 @@ SOURCE_FILE STMT_LIST L_CURLY "{" WHITESPACE "\n " + EXPR_STMT + MATCH_EXPR + MATCH_KW "match" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_ARM + PAREN_PAT + L_PAREN "(" + RANGE_PAT + DOT2 ".." + LITERAL_PAT + LITERAL + INT_NUMBER "1" + R_PAREN ")" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + PAREN_PAT + L_PAREN "(" + RANGE_PAT + DOT2EQ "..=" + LITERAL_PAT + LITERAL + INT_NUMBER "3" + R_PAREN ")" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + PAREN_PAT + L_PAREN "(" + OR_PAT + RANGE_PAT + DOT2 ".." + LITERAL_PAT + LITERAL + INT_NUMBER "2" + WHITESPACE " " + PIPE "|" + WHITESPACE " " + LITERAL_PAT + LITERAL + INT_NUMBER "4" + R_PAREN ")" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE "\n\n " EXPR_STMT MATCH_EXPR MATCH_KW "match" @@ -468,6 +539,6 @@ SOURCE_FILE COMMA "," WHITESPACE "\n " R_CURLY "}" - WHITESPACE "\n" + WHITESPACE "\n\n" R_CURLY "}" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rs index b54354211d2dc..69e2da2cf046c 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rs @@ -1,4 +1,10 @@ fn main() { + match () { + (..1) => (), + (..=3) => (), + (..2 | 4) => (), + } + match 92 { 0 ... 100 => (), 101 ..= 200 => (), @@ -30,4 +36,5 @@ fn main() { (1.., _) => (), (..=2, _) => (), } + } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 1b86eac0129aa..05e0012586d5f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -13,13 +13,11 @@ rust-version.workspace = true doctest = false [dependencies] -temp-dir.workspace = true - paths.workspace = true # span = {workspace = true, default-features = false} does not work span = { path = "../span", version = "0.0.0", default-features = false} intern.workspace = true - +stdx.workspace = true [dev-dependencies] expect-test.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 3b9c345fc27f5..718eb47228eb8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -2,13 +2,12 @@ mod proc_macros; +use paths::{Utf8Path, Utf8PathBuf}; use rustc_codegen_ssa::back::metadata::DefaultMetadataLoader; use rustc_interface::util::rustc_version_str; use rustc_proc_macro::bridge; -use std::{fs, io, time::SystemTime}; -use temp_dir::TempDir; - -use paths::{Utf8Path, Utf8PathBuf}; +use std::{fs, io, path::Path, time::SystemTime}; +use stdx::tempfile::NamedTempFile; use crate::{ PanicMessage, ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, TrackedEnv, @@ -18,19 +17,20 @@ use crate::{ pub(crate) struct Expander { inner: ProcMacroLibrary, modified_time: SystemTime, + _file: NamedTempFile, } impl Expander { - pub(crate) fn new(temp_dir: &TempDir, lib: &Utf8Path) -> io::Result { + pub(crate) fn new(lib: &Utf8Path) -> io::Result { // Some libraries for dynamic loading require canonicalized path even when it is // already absolute let lib = lib.canonicalize_utf8()?; let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?; - let path = ensure_file_with_lock_free_access(temp_dir, &lib)?; - let library = ProcMacroLibrary::open(path.as_ref())?; + let file = ensure_file_with_lock_free_access(lib); + let library = ProcMacroLibrary::open(file.path())?; - Ok(Expander { inner: library, modified_time }) + Ok(Expander { inner: library, modified_time, _file: file }) } pub(crate) fn expand<'a, S: ProcMacroSrvSpan + 'a>( @@ -73,10 +73,10 @@ struct ProcMacroLibrary { } impl ProcMacroLibrary { - fn open(path: &Utf8Path) -> io::Result { + fn open(path: &Path) -> io::Result { let proc_macros = rustc_span::create_default_session_globals_then(|| { rustc_metadata::locator::get_proc_macros( - path.as_ref(), + path, &DefaultMetadataLoader, rustc_version_str().unwrap_or("unknown"), ) @@ -88,37 +88,25 @@ impl ProcMacroLibrary { /// Copy the dylib to temp directory to prevent locking in Windows #[cfg(windows)] -fn ensure_file_with_lock_free_access( - temp_dir: &TempDir, - path: &Utf8Path, -) -> io::Result { - use std::collections::hash_map::RandomState; - use std::hash::{BuildHasher, Hasher}; - +fn ensure_file_with_lock_free_access(path: Utf8PathBuf) -> NamedTempFile { if std::env::var("RA_DONT_COPY_PROC_MACRO_DLL").is_ok() { - return Ok(path.to_path_buf()); + return NamedTempFile::from_path(path.into_std_path_buf()); } - let mut to = Utf8Path::from_path(temp_dir.path()).unwrap().to_owned(); - - let file_name = path.file_stem().ok_or_else(|| { - io::Error::new(io::ErrorKind::InvalidInput, format!("File path is invalid: {path}")) - })?; + (|| { + let file_name = path.file_stem().ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, format!("File path is invalid: {path}")) + })?; - to.push({ - // Generate a unique number by abusing `HashMap`'s hasher. - // Maybe this will also "inspire" a libs team member to finally put `rand` in libstd. - let unique_name = RandomState::new().build_hasher().finish(); - format!("{file_name}-{unique_name}.dll") - }); - fs::copy(path, &to)?; - Ok(to) + NamedTempFile::new_from_existing( + &format!("proc-macro-srv-{file_name}.dll"), + path.as_std_path(), + ) + })() + .unwrap_or_else(|_err| NamedTempFile::from_path(path.into_std_path_buf())) } #[cfg(unix)] -fn ensure_file_with_lock_free_access( - _temp_dir: &TempDir, - path: &Utf8Path, -) -> io::Result { - Ok(path.to_owned()) +fn ensure_file_with_lock_free_access(path: Utf8PathBuf) -> NamedTempFile { + NamedTempFile::from_path(path.into_std_path_buf()) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 28570e1af4426..7fc04a05155f8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -41,7 +41,6 @@ use std::{ use paths::{Utf8Path, Utf8PathBuf}; use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span}; -use temp_dir::TempDir; pub use crate::server_impl::token_id::SpanId; @@ -64,16 +63,11 @@ pub const RUSTC_VERSION_STRING: &str = env!("RUSTC_VERSION"); pub struct ProcMacroSrv<'env> { expanders: Mutex>>, env: &'env EnvSnapshot, - temp_dir: TempDir, } impl<'env> ProcMacroSrv<'env> { pub fn new(env: &'env EnvSnapshot) -> Self { - Self { - expanders: Default::default(), - env, - temp_dir: TempDir::with_prefix("proc-macro-srv").unwrap(), - } + Self { expanders: Default::default(), env } } pub fn join_spans(&self, first: Span, second: Span) -> Option { @@ -205,7 +199,7 @@ impl ProcMacroSrv<'_> { fn expander(&self, path: &Utf8Path) -> Result, String> { let expander = || { - let expander = dylib::Expander::new(&self.temp_dir, path) + let expander = dylib::Expander::new(path) .map_err(|err| format!("Cannot create expander for {path}: {err}",)); expander.map(Arc::new) }; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 9780bcf3481b4..7f92c66fb69d5 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -56,7 +56,7 @@ fn assert_expand_impl( expect_spanned: Expect, ) { let path = proc_macro_test_dylib_path(); - let expander = dylib::Expander::new(&temp_dir::TempDir::new().unwrap(), &path).unwrap(); + let expander = dylib::Expander::new(&path).unwrap(); let def_site = SpanId(0); let call_site = SpanId(1); @@ -186,7 +186,7 @@ pub fn assert_expand_with_callback( expect_spanned: Expect, ) { let path = proc_macro_test_dylib_path(); - let expander = dylib::Expander::new(&temp_dir::TempDir::new().unwrap(), &path).unwrap(); + let expander = dylib::Expander::new(&path).unwrap(); let def_site = Span { range: TextRange::new(0.into(), 150.into()), diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index f825a456dea70..86ae3e837d1a8 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -20,7 +20,6 @@ semver.workspace = true serde_json.workspace = true serde.workspace = true serde_derive.workspace = true -temp-dir.workspace = true toml.workspace = true tracing = { workspace = true, features = ["attributes"] } triomphe.workspace = true diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs index defd9f96ab5fb..126c0b41bb9f4 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs @@ -1,6 +1,7 @@ //! Read `.cargo/config.toml` as a TOML table use paths::{AbsPath, Utf8Path, Utf8PathBuf}; use rustc_hash::FxHashMap; +use stdx::tempfile::NamedTempDir; use toml::{ Spanned, de::{DeTable, DeValue}, @@ -139,7 +140,7 @@ impl<'a> CargoConfigFileReader<'a> { pub(crate) struct LockfileCopy { pub(crate) path: Utf8PathBuf, pub(crate) usage: LockfileUsage, - _temp_dir: temp_dir::TempDir, + _temp_dir: NamedTempDir, } pub(crate) enum LockfileUsage { @@ -193,22 +194,11 @@ pub(crate) fn make_lockfile_copy( return None; }; - let temp_dir = temp_dir::TempDir::with_prefix("rust-analyzer").ok()?; - let path: Utf8PathBuf = temp_dir.path().join("Cargo.lock").try_into().ok()?; - let path = match std::fs::copy(lockfile_path, &path) { - Ok(_) => { - tracing::debug!("Copied lock file from `{}` to `{}`", lockfile_path, path); - path - } - // lockfile does not yet exist, so we can just create a new one in the temp dir - Err(e) if e.kind() == std::io::ErrorKind::NotFound => path, - Err(e) => { - tracing::warn!("Failed to copy lock file from `{lockfile_path}` to `{path}`: {e}",); - return None; - } - }; + let temp_dir = NamedTempDir::new("rust-analyzer").ok()?; + let path = temp_dir.path().join("Cargo.lock"); + std::fs::copy(lockfile_path.as_std_path(), &path).ok()?; - Some(LockfileCopy { path, usage, _temp_dir: temp_dir }) + Some(LockfileCopy { path: Utf8PathBuf::from_path_buf(path).ok()?, usage, _temp_dir: temp_dir }) } #[test] diff --git a/src/tools/rust-analyzer/crates/stdx/src/lib.rs b/src/tools/rust-analyzer/crates/stdx/src/lib.rs index 275e0e5ac8db1..dcba06415b5f3 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/lib.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/lib.rs @@ -13,6 +13,7 @@ pub mod non_empty_vec; pub mod panic_context; pub mod process; pub mod rand; +pub mod tempfile; pub mod thread; pub mod variance; diff --git a/src/tools/rust-analyzer/crates/stdx/src/tempfile.rs b/src/tools/rust-analyzer/crates/stdx/src/tempfile.rs new file mode 100644 index 0000000000000..fe9ae83ef539e --- /dev/null +++ b/src/tools/rust-analyzer/crates/stdx/src/tempfile.rs @@ -0,0 +1,189 @@ +//! A temporary named file that will be deleted on drop, and on operating systems that support that, +//! also when the process exits (including being killed). + +use std::{ + fs::File, + io, + path::{Path, PathBuf}, +}; + +pub struct NamedTempFile { + _file: Option, + path: PathBuf, + delete_on_drop: bool, +} + +impl NamedTempFile { + pub fn new(prefix: &str) -> io::Result { + imp::create(prefix) + } + + /// Creates a new `NamedTempFile` that is a copy of an existing file. + pub fn new_from_existing(prefix: &str, existing: &Path) -> io::Result { + let result = NamedTempFile::new(prefix)?; + std::fs::copy(existing, &result.path)?; + Ok(result) + } + + /// Creates a `NamedTempFile` from a path, without deleting it on drop. + #[inline] + pub fn from_path(path: PathBuf) -> NamedTempFile { + NamedTempFile { _file: None, path, delete_on_drop: false } + } + + #[inline] + pub fn path(&self) -> &Path { + &self.path + } +} + +impl Drop for NamedTempFile { + fn drop(&mut self) { + if self.delete_on_drop && std::fs::remove_file(&self.path).is_err() { + tracing::info!("cannot remove temporary file {}", self.path.display()); + } + } +} + +pub struct NamedTempDir { + path: PathBuf, +} + +impl NamedTempDir { + pub fn new(prefix: &str) -> io::Result { + general_imp::create(prefix, |_options, path| std::fs::create_dir(path)) + .map(|((), path)| NamedTempDir { path }) + } + + #[inline] + pub fn path(&self) -> &Path { + &self.path + } +} + +impl Drop for NamedTempDir { + fn drop(&mut self) { + if std::fs::remove_dir_all(&self.path).is_err() { + tracing::info!("cannot remove temporary directory {}", self.path.display()); + } + } +} + +mod general_imp { + use std::{ + fs::OpenOptions, + io::{self, ErrorKind}, + path::{Path, PathBuf}, + sync::atomic::{AtomicU32, Ordering}, + }; + + static INTERNAL_COUNTER: AtomicU32 = AtomicU32::new(0); + + pub(super) fn create( + prefix: &str, + mut create: impl FnMut(OpenOptions, &Path) -> io::Result, + ) -> io::Result<(T, PathBuf)> { + let temp_dir = std::env::temp_dir().canonicalize()?; + let pid = std::process::id(); + loop { + let path = temp_dir.join(format!( + "{prefix}{pid:x}-{:x}", + INTERNAL_COUNTER.fetch_add(1, Ordering::AcqRel), + )); + let mut open_options = OpenOptions::new(); + open_options.create_new(true); + match create(open_options, &path) { + Err(e) if e.kind() == ErrorKind::AlreadyExists => {} + Err(e) => { + return Err(io::Error::new( + e.kind(), + format!("error creating directory {path:?}: {e}"), + )); + } + Ok(file) => { + return Ok((file, path)); + } + } + } + } +} + +#[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", +))] +mod imp { + use std::{ + ffi::CString, + io, + os::{ + fd::{AsRawFd, RawFd}, + unix::ffi::OsStrExt, + }, + }; + + use super::*; + + #[cfg(target_os = "linux")] + fn path_after_unlink(fd: RawFd) -> PathBuf { + PathBuf::from(format!("/proc/self/fd/{fd}")) + } + + #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] + fn path_after_unlink(fd: RawFd) -> PathBuf { + PathBuf::from(format!("/dev/fd/{fd}")) + } + + pub(super) fn create(prefix: &str) -> io::Result { + let (file, mut path) = general_imp::create(prefix, |options, path| options.open(path))?; + let mut delete_on_drop = true; + if let Ok(original_path) = CString::new(path.as_os_str().as_bytes()) { + // Unlinking the file will *not* remove it per the POSIX specification since it is open. + // We cannot use `std::fs::remove_file()`, since, while currently using `unlink()`, it does + // not guarantee it will use it. + if unsafe { libc::unlink(original_path.as_ptr()) } == 0 { + path = path_after_unlink(file.as_raw_fd()); + delete_on_drop = false; + } + } + Ok(NamedTempFile { _file: Some(file), path, delete_on_drop }) + } +} + +#[cfg(windows)] +mod imp { + use std::os::windows::fs::OpenOptionsExt; + + use super::*; + + const FILE_ATTRIBUTE_TEMPORARY: u32 = 0x100; + const FILE_FLAG_DELETE_ON_CLOSE: u32 = 0x04000000; + + pub(super) fn create(prefix: &str) -> io::Result { + let (file, path) = general_imp::create(prefix, |mut options, path| { + options + .attributes(FILE_ATTRIBUTE_TEMPORARY) + .custom_flags(FILE_FLAG_DELETE_ON_CLOSE) + .open(path) + })?; + Ok(NamedTempFile { _file: Some(file), path, delete_on_drop: false }) + } +} + +#[cfg(not(any( + target_os = "linux", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + windows, +)))] +mod imp { + use super::*; + + pub(super) fn create(prefix: &str) -> io::Result { + let (file, path) = general_imp::create(prefix, |options, path| options.open(path))?; + Ok(NamedTempFile { _file: Some(file), path, delete_on_drop: true }) + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 9017bae474273..36f7b56e3cd2a 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -1383,7 +1383,7 @@ fn expr_from_text_with_edition + AstNode>(text: &str, edition Some(it) => it, None => { let node = std::any::type_name::(); - panic!("Failed to make ast node `{node}` from text {text}") + panic!("Failed to make expr node `{node}` from text `{text}`") } }; let node = node.clone_subtree(); @@ -1403,7 +1403,7 @@ fn ast_from_text_with_edition(text: &str, edition: Edition) -> N { Some(it) => it, None => { let node = std::any::type_name::(); - panic!("Failed to make ast node `{node}` from text {text}") + panic!("Failed to make ast node `{node}` from text `{text}`") } }; let node = node.clone_subtree(); diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index 9fab8716b412f..35e9b8d2f87d0 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -528,7 +528,7 @@ mod tests { Some(it) => it, None => { let node = std::any::type_name::(); - panic!("Failed to make ast node `{node}` from text {text}") + panic!("Failed to make ast node `{node}` from text `{text}`") } }; let node = node.clone_subtree(); diff --git a/src/tools/rust-analyzer/crates/tt/src/storage.rs b/src/tools/rust-analyzer/crates/tt/src/storage.rs index 150777cc39e55..ba7a661b5e8a8 100644 --- a/src/tools/rust-analyzer/crates/tt/src/storage.rs +++ b/src/tools/rust-analyzer/crates/tt/src/storage.rs @@ -12,9 +12,6 @@ use std::{assert_matches, collections::hash_map, fmt::Debug, hint::cold_path, me #[cfg(all(debug_assertions, not(miri)))] use std::cell::Cell; -#[cfg(not(all(debug_assertions, not(miri))))] -use std::mem::MaybeUninit; - use intern::Symbol; use rustc_hash::FxHashMap; use span::{Span, SpanAnchor, SyntaxContext, TextRange, TextSize}; @@ -105,7 +102,7 @@ struct UninitBuffer { #[cfg(all(debug_assertions, not(miri)))] buffer: Box<[u8]>, #[cfg(not(all(debug_assertions, not(miri))))] - buffer: Box<[MaybeUninit]>, + buffer: Box<[std::mem::MaybeUninit]>, } impl UninitBuffer { @@ -807,10 +804,6 @@ unsafe fn decode_extended_span( } } -// FIXME: It'll probably be better to ensure this ourselves via a `#[repr(C, align(4))]` wrapper, even though practically -// this holds for all 32- and 64-bit targets (Rust does not guarantee this). -const _: () = assert!(align_of::<*const *const str>() >= 4); // Needed for the tagging of idents. - unsafe fn decode_symbol<'a>( mut ptr: BufferReader<'a>, first_byte: u8, @@ -830,8 +823,6 @@ unsafe fn decode_symbol<'a>( (ptr, symbols[symbol_idx as usize].clone()) } -/// We need `MaybeUninit` to preserve provenance. -/// /// The returned `u32` is the length of the children *in bytes*, if we read a subtree. Otherwise it's zero. unsafe fn decode<'a>( mut ptr: BufferReader<'a>, diff --git a/tests/assembly-llvm/naked-functions/target-feature.rs b/tests/assembly-llvm/naked-functions/target-feature.rs new file mode 100644 index 0000000000000..500e2a778e475 --- /dev/null +++ b/tests/assembly-llvm/naked-functions/target-feature.rs @@ -0,0 +1,165 @@ +//@ revisions: aarch64-elf aarch64-macho aarch64-coff x86_64 s390x riscv64 powerpc64 loongarch64 +//@ add-minicore +//@ assembly-output: emit-asm +//@ min-llvm-version: 23 +// +//@ [x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@ [x86_64] needs-llvm-components: x86 +// +//@ [aarch64-elf] compile-flags: --target aarch64-unknown-linux-gnu +//@ [aarch64-elf] needs-llvm-components: aarch64 +//@ [aarch64-macho] compile-flags: --target aarch64-apple-darwin +//@ [aarch64-macho] needs-llvm-components: aarch64 +//@ [aarch64-coff] compile-flags: --target aarch64-pc-windows-gnullvm +//@ [aarch64-coff] needs-llvm-components: aarch64 +// +//@ [s390x] compile-flags: --target s390x-unknown-linux-gnu +//@ [s390x] needs-llvm-components: systemz +// +//@ [powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu +//@ [powerpc64] needs-llvm-components: powerpc +// +//@ [riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu +//@ [riscv64] needs-llvm-components: riscv +// +// NOTE: loongarch64 does not error when using an instruction without enabling the corresponding +// target feature. +//@ [loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu +//@ [loongarch64] needs-llvm-components: loongarch + +// Test that the #[target_feature(enable = ...)]` works on naked functions. + +#![crate_type = "lib"] +#![feature(no_core, naked_functions_target_feature)] +#![feature(s390x_target_feature, powerpc_target_feature, loongarch_target_feature)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// x86_64-LABEL: vpclmulqdq: +// x86_64: vpclmulqdq +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "vpclmulqdq")] +unsafe extern "C" fn vpclmulqdq() { + naked_asm!("vpclmulqdq zmm1, zmm2, zmm3, 4") +} + +// i8mm is not enabled by default +// +// note that aarch64-apple-darwin enables more features than aarch64-unknown-linux-gnu +// +// aarch64-elf-LABEL: i8mm: +// aarch64-elf: usdot +// aarch64-macho-LABEL: i8mm: +// aarch64-macho: usdot +// aarch64-coff-LABEL: i8mm: +// aarch64-coff: usdot +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "i8mm")] +unsafe extern "C" fn i8mm() { + naked_asm!("usdot v0.4s, v1.16b, v2.4b[3]") +} + +// riscv64: sh1add: +// riscv64: sh1add +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "riscv64")] +#[target_feature(enable = "zba")] +unsafe extern "C" fn sh1add() { + naked_asm!("sh1add a0, a1, a2", "ret"); +} + +#[cfg(target_arch = "s390x")] +mod s390x { + use super::*; + + // s390x: vector: + // s390x: vavglg + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector")] + unsafe extern "C" fn vector() { + naked_asm!("vavglg %v0, %v0, %v0") + } + + // s390x: vector_enhancements_1: + // s390x: vfcesbs + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector-enhancements-1")] + unsafe extern "C" fn vector_enhancements_1() { + naked_asm!("vfcesbs %v0, %v0, %v0") + } + + // s390x: vector_enhancements_2: + // s390x: vclfp + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector-enhancements-2")] + unsafe extern "C" fn vector_enhancements_2() { + naked_asm!("vclfp %v0, %v0, 0, 0, 0") + } + + // s390x: vector_packed_decimal: + // s390x: vlrlr + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector-packed-decimal")] + unsafe extern "C" fn vector_packed_decimal() { + naked_asm!("vlrlr %v24, %r3, 0(%r2)", "br %r14") + } + + // s390x: vector_packed_decimal_enhancement: + // s390x: vcvbg + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector-packed-decimal-enhancement")] + unsafe extern "C" fn vector_packed_decimal_enhancement() { + naked_asm!("vcvbg %r0, %v0, 0, 1") + } + + // s390x: vector_packed_decimal_enhancement_2: + // s390x: vupkzl + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector-packed-decimal-enhancement-2")] + unsafe extern "C" fn vector_packed_decimal_enhancement_2() { + naked_asm!("vupkzl %v0, %v0, 0") + } +} + +// powerpc64: power10_vector: +// powerpc64: xxpermx +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "powerpc64")] +#[target_feature(enable = "power10-vector")] +unsafe extern "C" fn power10_vector() { + naked_asm!("xxpermx 34, 0, 1, 2, 0", "blr") +} + +// loongarch64: lasx: +// loongarch64: xvadd.b +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "loongarch64")] +#[target_feature(enable = "lasx")] +unsafe extern "C" fn lasx() { + naked_asm!("xvadd.b $xr0, $xr0, $xr1", "ret") +} + +// wasm32: simd128: +// wasm32: i8x16.shuffle +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "wasm32")] +#[target_feature(enable = "simd128")] +unsafe extern "C" fn simd128() { + naked_asm!("i8x16.shuffle 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15", "return"); +} diff --git a/tests/rustdoc-html/async/async-move-doctest.rs b/tests/rustdoc-ui/doctest/async-move-doctest.rs similarity index 77% rename from tests/rustdoc-html/async/async-move-doctest.rs rename to tests/rustdoc-ui/doctest/async-move-doctest.rs index e18ec353533df..f491a9a04f851 100644 --- a/tests/rustdoc-html/async/async-move-doctest.rs +++ b/tests/rustdoc-ui/doctest/async-move-doctest.rs @@ -1,5 +1,7 @@ //@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ edition:2018 +//@ check-pass // Prior to setting the default edition for the doctest pre-parser, // this doctest would fail due to a fatal parsing error. diff --git a/tests/rustdoc-ui/doctest/async-move-doctest.stdout b/tests/rustdoc-ui/doctest/async-move-doctest.stdout new file mode 100644 index 0000000000000..4790438d4602f --- /dev/null +++ b/tests/rustdoc-ui/doctest/async-move-doctest.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/async-move-doctest.rs - (line 10) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/comment-in-doctest.rs b/tests/rustdoc-ui/doctest/comment-in-doctest.rs similarity index 89% rename from tests/rustdoc-html/comment-in-doctest.rs rename to tests/rustdoc-ui/doctest/comment-in-doctest.rs index e580aa2bb72c6..2caec5db9c920 100644 --- a/tests/rustdoc-html/comment-in-doctest.rs +++ b/tests/rustdoc-ui/doctest/comment-in-doctest.rs @@ -1,4 +1,6 @@ //@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass // comments, both doc comments and regular ones, used to trick rustdoc's doctest parser into // thinking that everything after it was part of the regular program. combined with the librustc_ast diff --git a/tests/rustdoc-ui/doctest/comment-in-doctest.stdout b/tests/rustdoc-ui/doctest/comment-in-doctest.stdout new file mode 100644 index 0000000000000..5cb97c53f37fd --- /dev/null +++ b/tests/rustdoc-ui/doctest/comment-in-doctest.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/comment-in-doctest.rs - (line 12) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/demo-allocator-54478.rs b/tests/rustdoc-ui/doctest/demo-allocator-54478.rs similarity index 93% rename from tests/rustdoc-html/demo-allocator-54478.rs rename to tests/rustdoc-ui/doctest/demo-allocator-54478.rs index 80acfc0ff58a1..073d83e11120e 100644 --- a/tests/rustdoc-html/demo-allocator-54478.rs +++ b/tests/rustdoc-ui/doctest/demo-allocator-54478.rs @@ -1,4 +1,9 @@ // https://github.com/rust-lang/rust/issues/54478 + +//@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + #![crate_name="foo"] // Issue #54478: regression test showing that we can demonstrate @@ -15,8 +20,6 @@ // decided to change `rustdoc` to behave more like the compiler's // default setting, by leaving off `-C prefer-dynamic`. -//@ compile-flags:--test - //! This is a doc comment //! //! ```rust diff --git a/tests/rustdoc-ui/doctest/demo-allocator-54478.stdout b/tests/rustdoc-ui/doctest/demo-allocator-54478.stdout new file mode 100644 index 0000000000000..f32d9a5b7d932 --- /dev/null +++ b/tests/rustdoc-ui/doctest/demo-allocator-54478.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/demo-allocator-54478.rs - (line 25) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doc-cfg/doc-cfg-target-feature.rs b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.rs similarity index 78% rename from tests/rustdoc-html/doc-cfg/doc-cfg-target-feature.rs rename to tests/rustdoc-ui/doctest/doc-cfg-target-feature.rs index b66e86e36af8b..99a133a6829c5 100644 --- a/tests/rustdoc-html/doc-cfg/doc-cfg-target-feature.rs +++ b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.rs @@ -1,6 +1,7 @@ //@ only-x86_64 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ compile-flags:--test -//@ should-fail +//@ failure-status: 101 // #49723: rustdoc didn't add target features when extracting or running doctests diff --git a/tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout new file mode 100644 index 0000000000000..d71b1032e60ec --- /dev/null +++ b/tests/rustdoc-ui/doctest/doc-cfg-target-feature.stdout @@ -0,0 +1,39 @@ + +running 1 test +test $DIR/doc-cfg-target-feature.rs - foo (line 14) ... FAILED + +failures: + +---- $DIR/doc-cfg-target-feature.rs - foo (line 14) stdout ---- +warning: the feature `cfg_target_feature` has been stable since 1.27.0 and no longer requires an attribute to enable + --> $DIR/doc-cfg-target-feature.rs:14:12 + | +LL | #![feature(cfg_target_feature)] + | ^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(stable_features)]` on by default + +warning: 1 warning emitted + +Test executable failed (exit status: 101). + +stderr: + +thread 'main' ($TID) panicked at $DIR/doc-cfg-target-feature.rs:7:1: +assertion failed: false +stack backtrace: + 0: __rustc::rust_begin_unwind + 1: core::panicking::panic_fmt + 2: core::panicking::panic + 3: rust_out::main::_doctest_main__home_imperio_rust_rust_tests_rustdoc_ui_doctest_doc_cfg_target_feature_rs_14_0 + 4: rust_out::main + 5: >::call_once +note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. + + + +failures: + $DIR/doc-cfg-target-feature.rs - foo (line 14) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doc-test-attr-18199.rs b/tests/rustdoc-ui/doctest/doc-test-attr-18199.rs similarity index 74% rename from tests/rustdoc-html/doc-test-attr-18199.rs rename to tests/rustdoc-ui/doctest/doc-test-attr-18199.rs index 64016e32eeeb1..8350f244fccac 100644 --- a/tests/rustdoc-html/doc-test-attr-18199.rs +++ b/tests/rustdoc-ui/doctest/doc-test-attr-18199.rs @@ -1,6 +1,9 @@ -//@ compile-flags:--test // https://github.com/rust-lang/rust/issues/18199 +//@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + #![doc(test(attr(feature(staged_api))))] /// ``` diff --git a/tests/rustdoc-ui/doctest/doc-test-attr-18199.stdout b/tests/rustdoc-ui/doctest/doc-test-attr-18199.stdout new file mode 100644 index 0000000000000..a182a3b911af6 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doc-test-attr-18199.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/doc-test-attr-18199.rs - foo (line 9) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/edition-doctest.rs b/tests/rustdoc-ui/doctest/edition-doctest.rs similarity index 87% rename from tests/rustdoc-html/edition-doctest.rs rename to tests/rustdoc-ui/doctest/edition-doctest.rs index f43c074f806bd..066475dae7bf0 100644 --- a/tests/rustdoc-html/edition-doctest.rs +++ b/tests/rustdoc-ui/doctest/edition-doctest.rs @@ -1,4 +1,6 @@ -//@ compile-flags:--test +//@ compile-flags:--test --test-args=--test-threads=1 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass /// ```rust,edition2018 /// #![feature(try_blocks)] diff --git a/tests/rustdoc-ui/doctest/edition-doctest.stdout b/tests/rustdoc-ui/doctest/edition-doctest.stdout new file mode 100644 index 0000000000000..40d0df0575a76 --- /dev/null +++ b/tests/rustdoc-ui/doctest/edition-doctest.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test $DIR/edition-doctest.rs - foo (line 24) - compile fail ... ok +test $DIR/edition-doctest.rs - foo (line 5) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/edition-flag.rs b/tests/rustdoc-ui/doctest/edition-flag.rs similarity index 63% rename from tests/rustdoc-html/edition-flag.rs rename to tests/rustdoc-ui/doctest/edition-flag.rs index c57c8d50b2357..51235634dbf4a 100644 --- a/tests/rustdoc-html/edition-flag.rs +++ b/tests/rustdoc-ui/doctest/edition-flag.rs @@ -1,5 +1,7 @@ //@ compile-flags:--test //@ edition:2018 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass /// ```rust /// fn main() { diff --git a/tests/rustdoc-ui/doctest/edition-flag.stdout b/tests/rustdoc-ui/doctest/edition-flag.stdout new file mode 100644 index 0000000000000..4833a6dcf9adf --- /dev/null +++ b/tests/rustdoc-ui/doctest/edition-flag.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/edition-flag.rs - main (line 6) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/force-target-feature.rs b/tests/rustdoc-ui/doctest/force-target-feature.rs similarity index 64% rename from tests/rustdoc-html/force-target-feature.rs rename to tests/rustdoc-ui/doctest/force-target-feature.rs index fa71bbeea2747..c3f9798147074 100644 --- a/tests/rustdoc-html/force-target-feature.rs +++ b/tests/rustdoc-ui/doctest/force-target-feature.rs @@ -1,6 +1,9 @@ //@ only-x86_64 //@ compile-flags:--test -C target-feature=+avx -//@ should-fail +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ failure-status: 101 + +#![feature(doc_cfg)] /// (written on a spider's web) Some Struct /// diff --git a/tests/rustdoc-ui/doctest/force-target-feature.stdout b/tests/rustdoc-ui/doctest/force-target-feature.stdout new file mode 100644 index 0000000000000..861a742075623 --- /dev/null +++ b/tests/rustdoc-ui/doctest/force-target-feature.stdout @@ -0,0 +1,27 @@ + +running 1 test +test $DIR/force-target-feature.rs - SomeStruct (line 10) ... FAILED + +failures: + +---- $DIR/force-target-feature.rs - SomeStruct (line 10) stdout ---- +Test executable failed (exit status: 101). + +stderr: + +thread 'main' ($TID) panicked at $DIR/force-target-feature.rs:3:1: +oh no +stack backtrace: + 0: std::panicking::begin_panic::<&str> + 1: rust_out::main::_doctest_main__home_imperio_rust_rust_tests_rustdoc_ui_doctest_force_target_feature_rs_10_0 + 2: rust_out::main + 3: >::call_once +note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. + + + +failures: + $DIR/force-target-feature.rs - SomeStruct (line 10) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/ice-type-error-19181.rs b/tests/rustdoc-ui/doctest/ice-type-error-19181.rs similarity index 65% rename from tests/rustdoc-html/ice-type-error-19181.rs rename to tests/rustdoc-ui/doctest/ice-type-error-19181.rs index 02c6404762222..accb9e2cab1f4 100644 --- a/tests/rustdoc-html/ice-type-error-19181.rs +++ b/tests/rustdoc-ui/doctest/ice-type-error-19181.rs @@ -1,4 +1,7 @@ //@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + // https://github.com/rust-lang/rust/issues/19181 // rustdoc should not panic when target crate has compilation errors diff --git a/tests/rustdoc-ui/doctest/ice-type-error-19181.stdout b/tests/rustdoc-ui/doctest/ice-type-error-19181.stdout new file mode 100644 index 0000000000000..7326c0a25a069 --- /dev/null +++ b/tests/rustdoc-ui/doctest/ice-type-error-19181.stdout @@ -0,0 +1,5 @@ + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/no-run-still-checks-lints.rs b/tests/rustdoc-ui/doctest/no-run-still-checks-lints.rs similarity index 55% rename from tests/rustdoc-html/no-run-still-checks-lints.rs rename to tests/rustdoc-ui/doctest/no-run-still-checks-lints.rs index 73e311b72d5e5..cae6331f4723d 100644 --- a/tests/rustdoc-html/no-run-still-checks-lints.rs +++ b/tests/rustdoc-ui/doctest/no-run-still-checks-lints.rs @@ -1,5 +1,6 @@ //@ compile-flags:--test -//@ should-fail +//@ failure-status: 101 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" #![doc(test(attr(deny(warnings))))] diff --git a/tests/rustdoc-ui/doctest/no-run-still-checks-lints.stdout b/tests/rustdoc-ui/doctest/no-run-still-checks-lints.stdout new file mode 100644 index 0000000000000..86d1b4d3094b6 --- /dev/null +++ b/tests/rustdoc-ui/doctest/no-run-still-checks-lints.stdout @@ -0,0 +1,29 @@ + +running 1 test +test $DIR/no-run-still-checks-lints.rs - foo (line 7) - compile ... FAILED + +failures: + +---- $DIR/no-run-still-checks-lints.rs - foo (line 7) stdout ---- +error: unused variable: `a` + --> $DIR/no-run-still-checks-lints.rs:8:5 + | +LL | let a = 3; + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: the lint level is defined here + --> $DIR/no-run-still-checks-lints.rs:6:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]` + +error: aborting due to 1 previous error + +Couldn't compile the test. + +failures: + $DIR/no-run-still-checks-lints.rs - foo (line 7) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/process-termination.rs b/tests/rustdoc-ui/doctest/process-termination.rs similarity index 80% rename from tests/rustdoc-html/process-termination.rs rename to tests/rustdoc-ui/doctest/process-termination.rs index 73a86e57424a2..02ac594b3f0d4 100644 --- a/tests/rustdoc-html/process-termination.rs +++ b/tests/rustdoc-ui/doctest/process-termination.rs @@ -1,4 +1,6 @@ -//@ compile-flags:--test +//@ compile-flags:--test --test-args=--test-threads=1 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass /// A check of using various process termination strategies /// diff --git a/tests/rustdoc-ui/doctest/process-termination.stdout b/tests/rustdoc-ui/doctest/process-termination.stdout new file mode 100644 index 0000000000000..3e15b9a5df80a --- /dev/null +++ b/tests/rustdoc-ui/doctest/process-termination.stdout @@ -0,0 +1,8 @@ + +running 3 tests +test $DIR/process-termination.rs - check_process_termination (line 16) ... ok +test $DIR/process-termination.rs - check_process_termination (line 22) ... ok +test $DIR/process-termination.rs - check_process_termination (line 9) ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/sanitizer-option.rs b/tests/rustdoc-ui/doctest/sanitizer-option.rs similarity index 86% rename from tests/rustdoc-html/sanitizer-option.rs rename to tests/rustdoc-ui/doctest/sanitizer-option.rs index 7b0038138f09f..5f29f1b8bac7e 100644 --- a/tests/rustdoc-html/sanitizer-option.rs +++ b/tests/rustdoc-ui/doctest/sanitizer-option.rs @@ -1,7 +1,9 @@ //@ needs-sanitizer-support //@ needs-sanitizer-address //@ compile-flags: --test -Z sanitizer=address -C unsafe-allow-abi-mismatch=sanitizer -// +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + // #43031: Verify that rustdoc passes `-Z` options to rustc. Use an extern // function that is provided by the sanitizer runtime, if flag is not passed // correctly, then linking will fail. diff --git a/tests/rustdoc-html/test_option_check/test.rs b/tests/rustdoc-ui/doctest/test-option-check-2.rs similarity index 54% rename from tests/rustdoc-html/test_option_check/test.rs rename to tests/rustdoc-ui/doctest/test-option-check-2.rs index af7a5827690f0..2e74da1eca794 100644 --- a/tests/rustdoc-html/test_option_check/test.rs +++ b/tests/rustdoc-ui/doctest/test-option-check-2.rs @@ -1,6 +1,9 @@ -//@ compile-flags: --test +//@ compile-flags: --test --test-args=--test-threads=1 //@ check-test-line-numbers-match +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass +#[path = "test-option-check.rs"] pub mod bar; /// This is a Foo; diff --git a/tests/rustdoc-ui/doctest/test-option-check-2.stdout b/tests/rustdoc-ui/doctest/test-option-check-2.stdout new file mode 100644 index 0000000000000..ab2db4938dfab --- /dev/null +++ b/tests/rustdoc-ui/doctest/test-option-check-2.stdout @@ -0,0 +1,8 @@ + +running 3 tests +test $DIR/test-option-check-2.rs - Bar (line 18) ... ok +test $DIR/test-option-check-2.rs - Foo (line 11) ... ok +test $DIR/test-option-check.rs - bar::foooo (line 8) ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/test_option_check/bar.rs b/tests/rustdoc-ui/doctest/test-option-check.rs similarity index 65% rename from tests/rustdoc-html/test_option_check/bar.rs rename to tests/rustdoc-ui/doctest/test-option-check.rs index 7c2309a79d4b9..e5d3350e3f981 100644 --- a/tests/rustdoc-html/test_option_check/bar.rs +++ b/tests/rustdoc-ui/doctest/test-option-check.rs @@ -1,5 +1,7 @@ //@ compile-flags: --test //@ check-test-line-numbers-match +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass /// This looks like another awesome test! /// diff --git a/tests/rustdoc-ui/doctest/test-option-check.stdout b/tests/rustdoc-ui/doctest/test-option-check.stdout new file mode 100644 index 0000000000000..38f949612a47a --- /dev/null +++ b/tests/rustdoc-ui/doctest/test-option-check.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/test-option-check.rs - foooo (line 8) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/lints/renamed-lint-still-applies.rs b/tests/rustdoc-ui/lints/renamed-lint-still-applies.rs deleted file mode 100644 index a4d3a4b497117..0000000000000 --- a/tests/rustdoc-ui/lints/renamed-lint-still-applies.rs +++ /dev/null @@ -1,10 +0,0 @@ -// compile-args: --crate-type lib -#![deny(broken_intra_doc_links)] -//~^ WARNING renamed to `rustdoc::broken_intra_doc_links` -//! [x] -//~^ ERROR unresolved link - -#![deny(rustdoc::non_autolinks)] -//~^ WARNING renamed to `rustdoc::bare_urls` -//! http://example.com -//~^ ERROR not a hyperlink diff --git a/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr b/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr index 88807dfb495d0..f4428ff6e5983 100644 --- a/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr +++ b/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr @@ -1,5 +1,5 @@ warning: lint `broken_intra_doc_links` has been renamed to `rustdoc::broken_intra_doc_links` - --> $DIR/renamed-lint-still-applies.rs:2:9 + --> $DIR/renamed-lint-still-applies.rs:3:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::broken_intra_doc_links` @@ -7,33 +7,33 @@ LL | #![deny(broken_intra_doc_links)] = note: `#[warn(renamed_and_removed_lints)]` on by default warning: lint `rustdoc::non_autolinks` has been renamed to `rustdoc::bare_urls` - --> $DIR/renamed-lint-still-applies.rs:7:9 + --> $DIR/renamed-lint-still-applies.rs:8:9 | LL | #![deny(rustdoc::non_autolinks)] | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::bare_urls` error: unresolved link to `x` - --> $DIR/renamed-lint-still-applies.rs:4:6 + --> $DIR/renamed-lint-still-applies.rs:5:6 | LL | //! [x] | ^ no item named `x` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` note: the lint level is defined here - --> $DIR/renamed-lint-still-applies.rs:2:9 + --> $DIR/renamed-lint-still-applies.rs:3:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ error: this URL is not a hyperlink - --> $DIR/renamed-lint-still-applies.rs:9:5 + --> $DIR/renamed-lint-still-applies.rs:10:5 | LL | //! http://example.com | ^^^^^^^^^^^^^^^^^^ | = note: bare URLs are not automatically turned into clickable links note: the lint level is defined here - --> $DIR/renamed-lint-still-applies.rs:7:9 + --> $DIR/renamed-lint-still-applies.rs:8:9 | LL | #![deny(rustdoc::non_autolinks)] | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs index 1bec8ac03c981..1d60b3e0733ec 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs @@ -746,7 +746,8 @@ struct SuggestionStyleTwice { #[derive(Subdiagnostic)] #[suggestion_hidden("example message", code = "")] -//~^ ERROR #[suggestion_hidden(...)]` is not a valid attribute +//~^ ERROR cannot find attribute `suggestion_hidden` in this scope +//~| ERROR derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute struct SuggestionStyleOldSyntax { #[primary_span] sub: Span, @@ -754,7 +755,8 @@ struct SuggestionStyleOldSyntax { #[derive(Subdiagnostic)] #[suggestion_hidden("example message", code = "", style = "normal")] -//~^ ERROR #[suggestion_hidden(...)]` is not a valid attribute +//~^ ERROR cannot find attribute `suggestion_hidden` in this scope +//~| ERROR derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute struct SuggestionStyleOldAndNewSyntax { #[primary_span] sub: Span, diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr index cf3c9dd9ce10d..23999437d2876 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr @@ -439,19 +439,15 @@ error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute | LL | #[suggestion_hidden("example message", code = "")] | ^ - | - = help: Use `#[suggestion(..., style = "hidden")]` instead error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive-inline.rs:756:1 + --> $DIR/subdiagnostic-derive-inline.rs:757:1 | LL | #[suggestion_hidden("example message", code = "", style = "normal")] | ^ - | - = help: Use `#[suggestion(..., style = "hidden")]` instead error: derive(Diagnostic): invalid suggestion style - --> $DIR/subdiagnostic-derive-inline.rs:764:52 + --> $DIR/subdiagnostic-derive-inline.rs:766:52 | LL | #[suggestion("example message", code = "", style = "foo")] | ^^^^^ @@ -459,25 +455,25 @@ LL | #[suggestion("example message", code = "", style = "foo")] = help: valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only` error: expected string literal - --> $DIR/subdiagnostic-derive-inline.rs:772:52 + --> $DIR/subdiagnostic-derive-inline.rs:774:52 | LL | #[suggestion("example message", code = "", style = 42)] | ^^ error: expected `=` - --> $DIR/subdiagnostic-derive-inline.rs:780:49 + --> $DIR/subdiagnostic-derive-inline.rs:782:49 | LL | #[suggestion("example message", code = "", style)] | ^ error: expected `=` - --> $DIR/subdiagnostic-derive-inline.rs:788:49 + --> $DIR/subdiagnostic-derive-inline.rs:790:49 | LL | #[suggestion("example message", code = "", style("foo"))] | ^ error: derive(Diagnostic): `#[primary_span]` is not a valid attribute - --> $DIR/subdiagnostic-derive-inline.rs:799:5 + --> $DIR/subdiagnostic-derive-inline.rs:801:5 | LL | #[primary_span] | ^ @@ -486,7 +482,7 @@ LL | #[primary_span] = help: to create a suggestion with multiple spans, use `#[multipart_suggestion]` instead error: derive(Diagnostic): suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive-inline.rs:796:1 + --> $DIR/subdiagnostic-derive-inline.rs:798:1 | LL | #[suggestion("example message", code = "")] | ^ @@ -545,5 +541,17 @@ error: cannot find attribute `bar` in this scope LL | #[bar("...")] | ^^^ -error: aborting due to 82 previous errors +error: cannot find attribute `suggestion_hidden` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:748:3 + | +LL | #[suggestion_hidden("example message", code = "")] + | ^^^^^^^^^^^^^^^^^ + +error: cannot find attribute `suggestion_hidden` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:757:3 + | +LL | #[suggestion_hidden("example message", code = "", style = "normal")] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 84 previous errors diff --git a/tests/ui/asm/naked-functions/target-feature-aarch64.rs b/tests/ui/asm/naked-functions/target-feature-aarch64.rs new file mode 100644 index 0000000000000..f82122f773ca0 --- /dev/null +++ b/tests/ui/asm/naked-functions/target-feature-aarch64.rs @@ -0,0 +1,47 @@ +//@ add-minicore +//@ build-fail +//@ revisions: vanilla sha3 +//@ compile-flags: --target aarch64-unknown-linux-gnu -Z deduplicate-diagnostics=yes +//@[sha3] compile-flags: -Ctarget-feature=+sha3 +//@ needs-llvm-components: aarch64 +//@ min-llvm-version: 23 + +#![crate_type = "lib"] +#![feature(no_core, naked_functions_target_feature)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// check that a naked function using target features does not keep these features enabled +// for subsequent asm blocks. + +#[no_mangle] +#[unsafe(naked)] +#[target_feature(enable = "i8mm")] +unsafe extern "C" fn a() { + naked_asm!("usdot v0.4s, v1.16b, v2.4b[3]") +} + +//~? ERROR instruction requires: i8mm + +#[no_mangle] +#[unsafe(naked)] +unsafe extern "C" fn c() { + naked_asm!("usdot v0.4s, v2.16b, v2.4b[3]") +} + +#[no_mangle] +#[unsafe(naked)] +#[target_feature(enable = "sha3")] +unsafe extern "C" fn d() { + naked_asm!("eor3 v0.16b, v1.16b, v2.16b, v3.16b") +} + +//[vanilla]~? ERROR instruction requires: sha3 + +#[no_mangle] +#[unsafe(naked)] +unsafe extern "C" fn b() { + naked_asm!("eor3 v0.16b, v1.16b, v2.16b, v3.16b") +} diff --git a/tests/ui/asm/naked-functions/target-feature-aarch64.sha3.stderr b/tests/ui/asm/naked-functions/target-feature-aarch64.sha3.stderr new file mode 100644 index 0000000000000..49a65eaadb904 --- /dev/null +++ b/tests/ui/asm/naked-functions/target-feature-aarch64.sha3.stderr @@ -0,0 +1,10 @@ +error: instruction requires: i8mm + | +note: instantiated into assembly here + --> :15:1 + | +LL | usdot v0.4s, v2.16b, v2.4b[3] + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/asm/naked-functions/target-feature-aarch64.vanilla.stderr b/tests/ui/asm/naked-functions/target-feature-aarch64.vanilla.stderr new file mode 100644 index 0000000000000..8ac31d19f5e3e --- /dev/null +++ b/tests/ui/asm/naked-functions/target-feature-aarch64.vanilla.stderr @@ -0,0 +1,18 @@ +error: instruction requires: sha3 + | +note: instantiated into assembly here + --> :6:1 + | +LL | eor3 v0.16b, v1.16b, v2.16b, v3.16b + | ^ + +error: instruction requires: i8mm + | +note: instantiated into assembly here + --> :15:1 + | +LL | usdot v0.4s, v2.16b, v2.4b[3] + | ^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/asm/naked-functions/target-feature-s390x.rs b/tests/ui/asm/naked-functions/target-feature-s390x.rs new file mode 100644 index 0000000000000..b0f806c4c0a16 --- /dev/null +++ b/tests/ui/asm/naked-functions/target-feature-s390x.rs @@ -0,0 +1,30 @@ +//@ add-minicore +//@ build-fail +//@ compile-flags: --target s390x-unknown-linux-gnu -Z deduplicate-diagnostics=yes +//@ needs-llvm-components: systemz +//@ min-llvm-version: 23 + +#![crate_type = "lib"] +#![feature(no_core, naked_functions_target_feature)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// check that a naked function using target features does not keep these features enabled +// for subsequent asm blocks. + +#[no_mangle] +#[unsafe(naked)] +#[target_feature(enable = "vector-packed-decimal")] +unsafe extern "C" fn a() { + naked_asm!("vlrlr %v24, %r3, 0(%r2)") +} + +//~? ERROR instruction requires: vector-packed-decimal + +#[no_mangle] +#[unsafe(naked)] +unsafe extern "C" fn b() { + naked_asm!("vlrlr %v24, %r3, 0(%r3)") +} diff --git a/tests/ui/asm/naked-functions/target-feature-s390x.stderr b/tests/ui/asm/naked-functions/target-feature-s390x.stderr new file mode 100644 index 0000000000000..84d60c43bc765 --- /dev/null +++ b/tests/ui/asm/naked-functions/target-feature-s390x.stderr @@ -0,0 +1,10 @@ +error: instruction requires: vector-packed-decimal + | +note: instantiated into assembly here + --> :6:1 + | +LL | vlrlr %v24, %r3, 0(%r3) + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/gca/path-to-non-type-const.rs b/tests/ui/const-generics/gca/path-to-non-type-const.rs index 9deb517095cbd..53382fe4aa247 100644 --- a/tests/ui/const-generics/gca/path-to-non-type-const.rs +++ b/tests/ui/const-generics/gca/path-to-non-type-const.rs @@ -1,7 +1,12 @@ //@ check-pass //@ compile-flags: -Znext-solver -#![feature(min_generic_const_args, macroless_generic_const_args, generic_const_args)] +#![feature( + min_generic_const_args, + macroless_generic_const_args, + generic_const_args, + inherent_associated_types +)] #![expect(incomplete_features)] trait Trait { @@ -21,6 +26,14 @@ impl Trait for GenericStructImpl { const PROJECTED: usize = A; } +impl StructImpl { + const INHERENT: usize = 1; +} + +impl GenericStructImpl { + const INHERENT: usize = A; +} + struct Struct; fn f() { @@ -31,4 +44,6 @@ fn main() { let _ = Struct::; let _ = Struct::<{ ::PROJECTED }>; let _ = Struct::<{ as Trait>::PROJECTED }>; + let _ = Struct::<{ StructImpl::INHERENT }>; + let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>; } diff --git a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs deleted file mode 100644 index d15341836e493..0000000000000 --- a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! This test should be part of path-to-non-type-const.rs, and should pass. However, we are holding -//! off on implementing paths to IACs until a refactoring of how IAC generics are represented. -//@ compile-flags: -Znext-solver - -#![feature( - inherent_associated_types, - min_generic_const_args, - generic_const_args, - macroless_generic_const_args -)] -#![expect(incomplete_features)] - -struct StructImpl; -struct GenericStructImpl; - -impl StructImpl { - const INHERENT: usize = 1; -} - -impl GenericStructImpl { - const INHERENT: usize = A; -} - -struct Struct; - -fn main() { - let _ = Struct::<{ StructImpl::INHERENT }>; - //~^ ERROR use of `const` in the type system not defined as `type const` - let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>; - //~^ ERROR use of `const` in the type system not defined as `type const` -} diff --git a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.stderr b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.stderr deleted file mode 100644 index af671fb614e31..0000000000000 --- a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error: use of `const` in the type system not defined as `type const` - --> $DIR/path-to-non-type-inherent-associated-const.rs:27:24 - | -LL | let _ = Struct::<{ StructImpl::INHERENT }>; - | ^^^^^^^^^^^^^^^^^^^^ - | -help: add `type` before `const` for `StructImpl::INHERENT` - | -LL | type const INHERENT: usize = 1; - | ++++ - -error: use of `const` in the type system not defined as `type const` - --> $DIR/path-to-non-type-inherent-associated-const.rs:29:24 - | -LL | let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add `type` before `const` for `GenericStructImpl::::INHERENT` - | -LL | type const INHERENT: usize = A; - | ++++ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/lint/forbid-error-capped.rs b/tests/ui/lint/forbid-error-capped.rs index e458ddf90746e..bfa72beac5828 100644 --- a/tests/ui/lint/forbid-error-capped.rs +++ b/tests/ui/lint/forbid-error-capped.rs @@ -1,5 +1,4 @@ //@ check-pass -// compile-args: --cap-lints=warn -Fwarnings // This checks that the forbid attribute checking is ignored when the forbidden // lint is capped. diff --git a/tests/ui/lint/forbid-error-capped.stderr b/tests/ui/lint/forbid-error-capped.stderr index 479e7b9412d57..3de8c2fe0ce61 100644 --- a/tests/ui/lint/forbid-error-capped.stderr +++ b/tests/ui/lint/forbid-error-capped.stderr @@ -1,5 +1,5 @@ warning: allow(unused) incompatible with previous forbid - --> $DIR/forbid-error-capped.rs:8:10 + --> $DIR/forbid-error-capped.rs:7:10 | LL | #![forbid(warnings)] | -------- `forbid` level set here @@ -14,7 +14,7 @@ warning: 1 warning emitted Future incompatibility report: Future breakage diagnostic: warning: allow(unused) incompatible with previous forbid - --> $DIR/forbid-error-capped.rs:8:10 + --> $DIR/forbid-error-capped.rs:7:10 | LL | #![forbid(warnings)] | -------- `forbid` level set here diff --git a/tests/ui/mir/issue-71793-inline-args-storage.rs b/tests/ui/mir/issue-71793-inline-args-storage.rs index 0ed4d4723731e..38ce28a035346 100644 --- a/tests/ui/mir/issue-71793-inline-args-storage.rs +++ b/tests/ui/mir/issue-71793-inline-args-storage.rs @@ -1,10 +1,10 @@ // Verifies that inliner emits StorageLive & StorageDead when introducing // temporaries for arguments, so that they don't become part of the coroutine. // Regression test for #71793. -// + //@ check-pass //@ edition:2018 -// compile-args: -Zmir-opt-level=3 +//@ compile-flags: -Zmir-opt-level=3 #![crate_type = "lib"]