diff --git a/Cargo.lock b/Cargo.lock index 36213a1e4e481..d0016796d16b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4716,6 +4716,7 @@ name = "rustc_query_impl" version = "0.0.0" dependencies = [ "measureme", + "parking_lot", "rustc_data_structures", "rustc_errors", "rustc_hir", diff --git a/compiler/rustc_borrowck/src/polonius/constraints.rs b/compiler/rustc_borrowck/src/polonius/constraints.rs index ce32b6ee99012..637068a9799d7 100644 --- a/compiler/rustc_borrowck/src/polonius/constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/constraints.rs @@ -3,6 +3,7 @@ use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::mir::{Body, Location}; use rustc_middle::ty::RegionVid; use rustc_mir_dataflow::points::PointIndex; +use tracing::debug; use crate::BorrowSet; use crate::constraints::OutlivesConstraint; @@ -253,6 +254,7 @@ fn compute_forward_successor( // 2. Otherwise, gather the edges due to explicit region liveness, when applicable. if !live_regions.contains(region, next_point) { + debug!(?region, ?next_point, "region isn't live at successor"); return None; } @@ -272,6 +274,8 @@ fn compute_forward_successor( .flatten() .unwrap_or(ConstraintDirection::Bidirectional); + debug!(?direction); + match direction { ConstraintDirection::Backward => { // Contravariant cases: loans flow in the inverse direction, but we're only interested @@ -300,6 +304,7 @@ fn compute_backward_successor( // Liveness flows into the regions live at the next point. So, in a backwards view, we'll link // the region from the current point, if it's live there, to the previous point. if !live_regions.contains(region, current_point) { + debug!(?region, ?current_point, "region isn't live at current point"); return None; } diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 5285f724b02ec..e0f4c9ff98eca 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -10,11 +10,16 @@ use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; use crate::constraints::OutlivesConstraint; +use crate::dataflow::BorrowIndex; use crate::polonius::{LocalizedConstraintGraphVisitor, LocalizedNode, PoloniusContext}; use crate::region_infer::values::LivenessValues; use crate::type_check::Locations; use crate::{BorrowckInferCtxt, ClosureRegionRequirements, RegionInferenceContext}; +/// The polonius MIR dump template: a regular HTML file for easy editing, with special dummy +/// sections to be replaced by real contents. +const TEMPLATE: &str = include_str!("./dump/polonius-mir-dump.template.html"); + /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. pub(crate) fn dump_polonius_mir<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, @@ -36,7 +41,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( // If we have a polonius graph to dump along the rest of the MIR and NLL info, we extract its // constraints here. - let mut collector = LocalizedOutlivesConstraintCollector { constraints: Vec::new() }; + let mut collector = MirDumpCollector::default(); if let Some(graph) = &polonius_context.graph { graph.traverse( body, @@ -72,7 +77,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( let _ = try { let mut file = dumper.create_dump_file("html", body)?; - emit_polonius_dump(&dumper, body, regioncx, borrow_set, &collector.constraints, &mut file)?; + emit_polonius_dump(&dumper, body, regioncx, borrow_set, &collector, &mut file)?; }; } @@ -84,12 +89,19 @@ struct LocalizedOutlivesConstraint { to: PointIndex, } -/// Visitor to record constraints encountered when traversing the localized constraint graph. -struct LocalizedOutlivesConstraintCollector { +/// Visitor to record constraints encountered when traversing the localized constraint graph, as +/// well as the reachability of each loan. +#[derive(Default)] +struct MirDumpCollector { constraints: Vec, + reachability: FxIndexMap>, } -impl LocalizedConstraintGraphVisitor for LocalizedOutlivesConstraintCollector { +impl LocalizedConstraintGraphVisitor for MirDumpCollector { + fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode) { + self.reachability.entry(loan).or_default().push(node); + } + fn on_successor_discovered(&mut self, current_node: LocalizedNode, successor: LocalizedNode) { self.constraints.push(LocalizedOutlivesConstraint { source: current_node.region, @@ -111,75 +123,77 @@ fn emit_polonius_dump<'tcx>( body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, borrow_set: &BorrowSet<'tcx>, - localized_outlives_constraints: &[LocalizedOutlivesConstraint], + collector: &MirDumpCollector, out: &mut dyn io::Write, ) -> io::Result<()> { - // Prepare the HTML dump file prologue. - writeln!(out, "")?; - writeln!(out, "")?; - writeln!(out, "Polonius MIR dump")?; - writeln!(out, "")?; - - // Section 1: the NLL + Polonius MIR. - writeln!(out, "
")?; - writeln!(out, "Raw MIR dump")?; - writeln!(out, "
")?;
-    emit_html_mir(dumper, body, out)?;
-    writeln!(out, "
")?; - writeln!(out, "
")?; - - // Section 2: mermaid visualization of the polonius constraint graph. - writeln!(out, "
")?; - writeln!(out, "Polonius constraint graph")?; - writeln!(out, "
")?;
-    let edge_count = emit_mermaid_constraint_graph(
-        borrow_set,
-        regioncx.liveness_constraints(),
-        &localized_outlives_constraints,
-        out,
-    )?;
-    writeln!(out, "
")?; - writeln!(out, "
")?; - - // Section 3: mermaid visualization of the CFG. - writeln!(out, "
")?; - writeln!(out, "Control-flow graph")?; - writeln!(out, "
")?;
-    emit_mermaid_cfg(body, out)?;
-    writeln!(out, "
")?; - writeln!(out, "
")?; - - // Section 4: mermaid visualization of the NLL region graph. - writeln!(out, "
")?; - writeln!(out, "NLL regions")?; - writeln!(out, "
")?;
-    emit_mermaid_nll_regions(dumper.tcx(), regioncx, out)?;
-    writeln!(out, "
")?; - writeln!(out, "
")?; - - // Section 5: mermaid visualization of the NLL SCC graph. - writeln!(out, "
")?; - writeln!(out, "NLL SCCs")?; - writeln!(out, "
")?;
-    emit_mermaid_nll_sccs(dumper.tcx(), regioncx, out)?;
-    writeln!(out, "
")?; - writeln!(out, "
")?; - - // Finalize the dump with the HTML epilogue. - writeln!( - out, - "" - )?; - writeln!(out, "")?; - writeln!(out, "")?; - writeln!(out, "")?; + let mut edge_count = 0; + + // We replace the dummy $SECTION tokens from the HTML polonius dump template, and emit the + // result into the given writer. + for chunk in TEMPLATE.split("$SECTION") { + match chunk.strip_prefix("_") { + None => { + // We're at the beginning of the template: this is the prologue to emit as-is. + writeln!(out, "{}", chunk)?; + } + Some(section) => { + // This is the start of a prefixed section, we look for its identifier. + let dummy_section_end = section + .find("<") + .expect("the template section end boundary needs to be present"); + let section_identifier = section[..dummy_section_end].trim(); + + // Emit the real section instead of the dummy token. + match section_identifier { + "MIR" => { + emit_html_mir(dumper, body, out)?; + } + "POLONIUS_CONSTRAINTS" => { + edge_count = emit_mermaid_constraint_graph( + borrow_set, + regioncx.liveness_constraints(), + &collector.constraints, + out, + )?; + } + "POLONIUS_REACHABILITY" => { + emit_loan_reachability( + borrow_set, + regioncx.liveness_constraints(), + &collector.reachability, + out, + )?; + } + "CFG" => { + emit_mermaid_cfg(body, out)?; + } + "NLL_CONSTRAINTS" => { + emit_mermaid_nll_regions(dumper.tcx(), regioncx, out)?; + } + "NLL_SCCS" => { + emit_mermaid_nll_sccs(dumper.tcx(), regioncx, out)?; + } + "INITIALIZATION" => { + writeln!(out, "")?; + } + + _ => { + unreachable!("unexpected dummy section identifier {:?}", section_identifier) + } + } + + // And finally, emit the contents that followed the dummy token. + writeln!(out, "{}", §ion[dummy_section_end..])?; + } + } + } Ok(()) } @@ -431,15 +445,9 @@ fn emit_mermaid_constraint_graph<'tcx>( localized_outlives_constraints: &[LocalizedOutlivesConstraint], out: &mut dyn io::Write, ) -> io::Result { - let location_name = |location: Location| { - // A MIR location looks like `bb5[2]`. As that is not a syntactically valid mermaid node id, - // transform it into `BB5_2`. - format!("BB{}_{}", location.block.index(), location.statement_index) - }; - let region_name = |region: RegionVid| format!("'{}", region.index()); - let node_name = |region: RegionVid, point: PointIndex| { + let node_label = |region: RegionVid, point: PointIndex| { let location = liveness.location_from_point(point); - format!("{}_{}", region_name(region), location_name(location)) + node_name(region, location) }; // The mermaid chart type: a top-down flowchart, which supports subgraphs. @@ -474,7 +482,7 @@ fn emit_mermaid_constraint_graph<'tcx>( for (region, points) in points_per_region { writeln!(out, " subgraph \"{}\"", region_name(region))?; for point in points { - writeln!(out, " {}", node_name(region, point))?; + writeln!(out, " {}", node_label(region, point))?; } writeln!(out, " end\n")?; } @@ -485,8 +493,8 @@ fn emit_mermaid_constraint_graph<'tcx>( writeln!( out, " {} --> {}", - node_name(constraint.source, constraint.from), - node_name(constraint.target, constraint.to), + node_label(constraint.source, constraint.from), + node_label(constraint.target, constraint.to), )?; } @@ -495,3 +503,70 @@ fn emit_mermaid_constraint_graph<'tcx>( let edge_count = borrow_set.len() + localized_outlives_constraints.len(); Ok(edge_count) } + +/// Emits the reachability of loans: a list of all nodes reached while traversing the polonius +/// constraint graph. +fn emit_loan_reachability( + borrow_set: &BorrowSet<'_>, + liveness: &LivenessValues, + reachability: &FxIndexMap>, + out: &mut dyn io::Write, +) -> io::Result<()> { + for (loan, _) in borrow_set.iter_enumerated() { + let Some(reachability) = reachability.get(&loan) else { + continue; + }; + let loan = format!("L{}", loan.index()); + + // The button to display the loan trace. The javascript event listener is hooked up in the + // template itself. + writeln!( + out, + "
" + )?; + + // The actual trace contents, hidden by default. + writeln!(out, "")?; + } + + Ok(()) +} + +fn region_name(region: RegionVid) -> String { + format!("'{}", region.index()) +} +/// A MIR location looks like `bb5[2]`. As that is not a syntactically valid mermaid node id, +/// transform it into `BB5_2`. +fn location_name(location: Location) -> String { + format!("BB{}_{}", location.block.index(), location.statement_index) +} +fn node_name(region: RegionVid, location: Location) -> String { + format!("{}_{}", region_name(region), location_name(location)) +} diff --git a/compiler/rustc_borrowck/src/polonius/dump/polonius-mir-dump.template.html b/compiler/rustc_borrowck/src/polonius/dump/polonius-mir-dump.template.html new file mode 100644 index 0000000000000..e2b9b412963a2 --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/dump/polonius-mir-dump.template.html @@ -0,0 +1,86 @@ + + + +Polonius MIR dump + + + + + + +
+
Raw MIR dump
+
$SECTION_MIR
+
+ + +
+
Polonius constraint graph
+
$SECTION_POLONIUS_CONSTRAINTS
+
+ + +
+
Loan Traces
+ $SECTION_POLONIUS_REACHABILITY +
+ + +
+
Control-flow graph
+
$SECTION_CFG
+
+ + +
+
NLL regions
+
$SECTION_NLL_CONSTRAINTS
+
+ + +
+
NLL SCCs
+
$SECTION_NLL_SCCS
+
+ + +$SECTION_INITIALIZATION + + + diff --git a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs index 0ae3ff3c04790..1aae724266c6f 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs @@ -192,7 +192,7 @@ pub(crate) fn emit_drop_facts<'tcx>( debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind); let Some(facts) = facts.as_mut() else { return }; let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); - tcx.for_each_free_region(kind, |drop_live_region| { + tcx.for_each_free_region(&kind, |drop_live_region| { let region_vid = universal_regions.to_region_vid(drop_live_region); facts.drop_of_var_derefs_origin.push((local, region_vid.into())); }); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index dc8e8e077be95..7f3ed58db6c77 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -5,8 +5,7 @@ use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::traits::TraitErrors; 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}; +use rustc_middle::ty::{GenericArg, Ty, TypeVisitable, TypeVisitableExt}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; @@ -536,7 +535,8 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { /// points `live_at`. fn add_use_live_facts_for(&mut self, value: Ty<'tcx>, live_at: &IntervalSet) { debug!("add_use_live_facts_for(value={:?})", value); - Self::make_all_regions_live(self.location_map, self.typeck, value, live_at); + Self::record_region_variance(self.typeck, value.into()); + Self::make_all_regions_live(self.location_map, self.typeck, value.into(), live_at); } /// Some variable with type `live_ty` is "drop live" at `location` @@ -577,6 +577,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { } } + // Since the entire dropped local is live, record the variance of its regions. + Self::record_region_variance(self.typeck, dropped_ty.into()); + // 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 { @@ -591,10 +594,25 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { } } + /// `live_kind` is the type of a (use- or drop-) live local. + /// Record the variance of any region(s) appearing in it for Polonius. Does + /// nothing if Polonius is not active. + fn record_region_variance(typeck: &mut TypeChecker<'_, 'tcx>, live_kind: GenericArg<'tcx>) { + // When using `-Zpolonius=next`, we record the variance of each live region. + if let Some(polonius_context) = typeck.polonius_context.as_mut() { + record_live_region_variance( + typeck.infcx.tcx, + &mut polonius_context.live_region_variances, + typeck.universal_regions, + live_kind, + ); + } + } + fn make_all_regions_live( location_map: &DenseLocationMap, typeck: &mut TypeChecker<'_, 'tcx>, - value: impl TypeVisitable> + Relate>, + value: GenericArg<'tcx>, live_at: &IntervalSet, ) { debug!("make_all_regions_live(value={:?})", value); @@ -608,20 +626,10 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { param_env: typeck.infcx.param_env, op: |r| { let live_region_vid = typeck.universal_regions.to_region_vid(r); - typeck.constraints.liveness_constraints.add_points(live_region_vid, live_at); }, }); - - // When using `-Zpolonius=next`, we record the variance of each live region. - if let Some(polonius_context) = typeck.polonius_context.as_mut() { - record_live_region_variance( - typeck.infcx.tcx, - &mut polonius_context.live_region_variances, - typeck.universal_regions, - value, - ); - } + Self::record_region_variance(typeck, value); } } diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 972a4cc8de83e..677db66530ecb 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -110,8 +110,7 @@ impl TestHarnessGenerator<'_> { Some(node_id), ); for test in &mut tests { - // See the comment on `mk_main` for why we're using - // `apply_mark` directly. + // See the comment on `add_main` for why we're using `apply_mark` directly. test.ident.span = test.ident.span.apply_mark(expn_id.to_expn_id(), Transparency::Opaque); } @@ -127,7 +126,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { self.add_test_cases(ast::CRATE_NODE_ID, c.spans.inner_span, prev_tests); // Create a main function to run our tests - c.items.push(mk_main(&mut self.cx)); + add_main(&mut self.cx, c); } fn visit_item(&mut self, item: &mut ast::Item) { @@ -288,16 +287,20 @@ fn generate_test_harness( /// [`TestCtxt::reexport_test_harness_main`] provides a different name for the `main` /// function and [`TestCtxt::test_runner`] provides a path that replaces /// `test::test_main_env_args`. -fn mk_main(cx: &mut TestCtxt<'_>) -> Box { +fn add_main(cx: &mut TestCtxt<'_>, c: &mut ast::Crate) { let sp = cx.def_site; let ecx = &cx.ext_cx; + // `sp` has def-site hygiene so should not clash with user-defined names. let test_ident = Ident::new(sym::test, sp); - let runner_name = - if cx.panic_strategy.unwinds() { "test_main_env_args" } else { "test_main_env_args_abort" }; - // test::test_main_env_args(...) let mut test_runner = cx.test_runner.clone().unwrap_or_else(|| { + // Built-in runner name depends on panic strategy. + let runner_name = if cx.panic_strategy.unwinds() { + "test_main_env_args" + } else { + "test_main_env_args_abort" + }; ecx.path(sp, vec![test_ident, Ident::from_str_and_span(runner_name, sp)]) }); @@ -308,10 +311,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box { let call_test_main = ecx.stmt_expr(call_test_main); // extern crate test - let test_extern_stmt = ecx.stmt_item( - sp, - ecx.item(sp, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, test_ident)), - ); + let test_extern_stmt = + ecx.item(sp, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, test_ident)); // #[rustc_main] let main_attr = ecx.attr_word(sym::rustc_main, sp); @@ -320,20 +321,18 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box { // #[doc(hidden)] let doc_hidden_attr = ecx.attr_nested_word(sym::doc, sym::hidden, sp); - // pub fn main() { ... } - // FIXME: it would be nice if we could use `std::process::ExitCode` as return type here, and - // remove all early-exit from libtest itself. Or rather, it should be `test::ExitCode` so we - // don't depend on whatever `std` may be. This needs the `extern crate test` to be *outside* - // `main`. But naively moving it out causes ICEs that give no hint as to what is wrong. - let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(ThinVec::new())); - - // If no test runner is provided we need to import the test crate - let main_body = if cx.test_runner.is_none() { - ecx.block(sp, thin_vec![test_extern_stmt, call_test_main]) + // pub fn main() -> ExitCode { ... } + let main_ret_ty = if cx.test_runner.is_none() { + // Built-in runner has return type `ExitCode`. + let exit_code_path = vec![test_ident, Ident::from_str_and_span("ExitCode", sp)]; + ecx.ty(sp, ast::TyKind::Path(None, ecx.path(sp, exit_code_path))) } else { - ecx.block(sp, thin_vec![call_test_main]) + // User-defined runners have return type `()`. + ecx.ty(sp, ast::TyKind::Tup(ThinVec::new())) }; + let main_body = ecx.block(sp, thin_vec![call_test_main]); + let decl = ecx.fn_decl(ThinVec::new(), ast::FnRetTy::Ty(main_ret_ty)); let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp }; let defaultness = ast::Defaultness::Implicit; @@ -365,8 +364,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box { }); // Integrate the new item into existing module structures. - let main = AstFragment::Items(smallvec![main]); - cx.ext_cx.monotonic_expander().fully_expand_fragment(main).make_items().pop().unwrap() + let items = AstFragment::Items(smallvec![test_extern_stmt, main]); + c.items.extend(cx.ext_cx.monotonic_expander().fully_expand_fragment(items).make_items()); } /// Creates a slice containing every test like so: diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 3b02910ff9c85..2554a518facd0 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -5,13 +5,13 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(trim_prefix_suffix))] #![feature(extern_types)] #![feature(file_buffered)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] #![feature(macro_derive)] #![feature(once_cell_try)] -#![feature(trim_prefix_suffix)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 5af05fa6fe0fd..697bf2d528036 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -5,11 +5,11 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(trim_prefix_suffix))] #![feature(decl_macro)] #![feature(file_buffered)] #![feature(panic_backtrace_config)] #![feature(panic_update_hook)] -#![feature(trim_prefix_suffix)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 8fde417b764d8..705bb780a3ecf 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -961,8 +961,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.require_lang_item(LangItem::Sized, ty_span), ); check_where_clauses(wfcx, def_id); - wfcheck::check_const_item(wfcx, def_id, ty); - Ok(()) + wfcheck::check_const_item(wfcx, def_id, ty) })); // Only `Node::Item` and `Node::ForeignItem` still have HIR based diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 9aded3aeb9318..30947ce0f4f98 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -47,8 +47,7 @@ use tracing::{debug, instrument}; use super::compare_eii::{compare_eii_function_types, compare_eii_statics}; use crate::autoderef::Autoderef; use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params}; -use crate::diagnostics; -use crate::diagnostics::InvalidReceiverTyHint; +use crate::diagnostics::{self, InvalidReceiverTyHint, ParamInTyOfConstParam}; pub(super) struct WfCheckingCtxt<'a, 'tcx> { pub(super) ocx: ObligationCtxt<'a, 'tcx, FulfillmentError<'tcx>>, @@ -517,9 +516,14 @@ pub(crate) fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) b, ) } - ty::ClauseKind::TypeOutlives(ty::OutlivesClause(a, b)) => { - !ty_known_to_outlive(tcx, gat_def_id, param_env, &FxIndexSet::default(), a, b) - } + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(a, b)) => !ty_known_to_outlive( + tcx, + gat_def_id, + param_env, + &FxIndexSet::default(), + Unnormalized::new_wip(a), + b, + ), _ => bug!("Unexpected ClauseKind"), }) .map(|clause| clause.to_string()) @@ -623,7 +627,14 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( // reflected in a where clause on the GAT itself. for (ty, ty_idx) in &types { // In our example, requires that `Self: 'a` - if ty_known_to_outlive(tcx, item_def_id, param_env, wf_tys, *ty, *region_a) { + if ty_known_to_outlive( + tcx, + item_def_id, + param_env, + wf_tys, + Unnormalized::new_wip(*ty), + *region_a, + ) { debug!(?ty_idx, ?region_a_idx); debug!("required clause: {ty} must outlive {region_a}"); // Translate into the generic parameters of the GAT. In @@ -927,7 +938,6 @@ pub(crate) fn check_associated_item( let ty = tcx.type_of(def_id).instantiate_identity(); let ty = wfcx.deeply_normalize(span, Some(WellFormedLoc::Ty(def_id)), ty); wfcx.register_wf_obligation(span, loc, ty.into()); - check_const_item(wfcx, def_id, ty); if item.defaultness(tcx).has_value() { let code = ObligationCauseCode::SizedConstOrStatic; @@ -939,7 +949,7 @@ pub(crate) fn check_associated_item( ); } - Ok(()) + check_const_item(wfcx, def_id, ty) } ty::AssocKind::Fn { .. } => { let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); @@ -1259,22 +1269,33 @@ pub(crate) fn check_static_item<'tcx>( } /// Runs checks common to both free consts and associated consts -#[instrument(level = "debug", skip(wfcx))] +#[instrument(level = "debug", skip(wfcx), ret)] pub(super) fn check_const_item<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: LocalDefId, item_ty: Ty<'tcx>, -) { +) -> Result<(), ErrorGuaranteed> { let tcx = wfcx.tcx(); let span = tcx.def_span(def_id); - if tcx.is_direct_const(def_id.into()) && !tcx.features().const_param_ty_unchecked() { - wfcx.register_bound( - ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(item_ty)), - wfcx.param_env, - item_ty, - tcx.require_lang_item(LangItem::ConstParamTy, span), - ); + let mut res = Ok(()); + + if tcx.is_direct_const(def_id.into()) { + if !tcx.features().const_param_ty_unchecked() { + wfcx.register_bound( + ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(item_ty)), + wfcx.param_env, + item_ty, + tcx.require_lang_item(LangItem::ConstParamTy, span), + ); + } + // FIXME(min_generic_const_args): We *might* want to move this check to `type_of`, so we can + // return `ty::Error` if it references invalid params. However, doing so is hard, because + // `type_of` doesn't know if it's a direct const - `const_of_item` determines that, and + // `const_of_item` calls `type_of`. + if !tcx.features().generic_const_parameter_types() && item_ty.has_param() { + res = Err(tcx.dcx().emit_err(ParamInTyOfConstParam { span, ty: item_ty })); + } } if let Some(direct_rhs) = tcx.const_of_item(def_id) { @@ -1289,6 +1310,8 @@ pub(super) fn check_const_item<'tcx>( ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(norm_ct, item_ty)), )); } + + res } #[instrument(level = "debug", skip(tcx, impl_))] 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 b659dd896aba0..081b66f6a3f2e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2396,16 +2396,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // we have the ability to intermix typeck of anon const const args with the parent // bodies typeck. - // FIXME(min_generic_const_args): This check should be removed for mGCA, it is due to - // the lack of ConstParamTy rib-checking in nameres for directly represented const - // items. - // We also error if the type contains any regions as effectively any region will wind // up as a region variable in mir borrowck. It would also be somewhat concerning if // hir typeck was using equality but mir borrowck wound up using subtyping as that could // result in a non-infer in hir typeck but a region variable in borrowck. - if (tcx.features().generic_const_parameter_types() - || tcx.features().min_generic_const_args()) + if tcx.features().generic_const_parameter_types() && (ty.has_free_regions() || ty.has_erased_regions()) { let e = self.dcx().span_err( @@ -2492,8 +2487,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) -> Const<'tcx> { let tcx = self.tcx(); - let (elem_ty, len) = match ty.kind() { - ty::Array(elem_ty, len) => (elem_ty, len), + let elem_ty = match ty.kind() { + ty::Array(elem_ty, _) => elem_ty, ty::Error(e) => return Const::new_error(tcx, *e), _ => { let e = tcx @@ -2509,28 +2504,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .map(|elem| self.lower_const_arg(elem, *elem_ty)) .collect::>(); - let len = tcx - .try_normalize_erasing_regions( - ty::TypingEnv::new(ty::ParamEnv::empty(), TypingMode::non_body_analysis()), - Unnormalized::new_wip(*len), - ) - .unwrap_or(*len); - if let Some(expected_len) = len.try_to_target_usize(tcx) - && expected_len != elems.len() as u64 - { - let e = tcx.dcx().span_err( - array_expr.span, - format!( - "expected array with {expected_len} elements, found {} elements", - array_expr.elems.len() - ), - ); - return Const::new_error(tcx, e); - } - + // The array len passed in the type might be an infer var, or a const param, or it could + // just be an incorrect constant. So, construct the resulting valtree's type based on the + // provided syntax rather than the expected type. The surrounding typeck will catch any + // mismatches. + let valtree_ty = Ty::new_array(tcx, *elem_ty, elems.len() as u64); let valtree = ty::ValTree::from_branches(tcx, elems); - - ty::Const::new_value(tcx, valtree, ty) + ty::Const::new_value(tcx, valtree, valtree_ty) } fn try_recover_misrepresented_function_call( diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 8a4100e2681ca..dd3b5d4429975 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -1,11 +1,11 @@ // tidy-alphabetical-start #![cfg_attr(bootstrap, feature(never_type))] +#![cfg_attr(bootstrap, feature(trim_prefix_suffix))] #![feature(deref_patterns)] #![feature(iter_intersperse)] #![feature(iter_order_by)] #![feature(option_into_flat_iter)] #![feature(option_reference_flattening)] -#![feature(trim_prefix_suffix)] // tidy-alphabetical-end mod _match; diff --git a/compiler/rustc_middle/src/query/job.rs b/compiler/rustc_middle/src/query/job.rs index 0f7fb7f6fe9f4..99ae3cac38ccd 100644 --- a/compiler/rustc_middle/src/query/job.rs +++ b/compiler/rustc_middle/src/query/job.rs @@ -35,21 +35,6 @@ impl<'tcx> QueryJob<'tcx> { pub fn new(id: QueryJobId, span: Span, parent: Option) -> Self { QueryJob { id, span, parent, latch: None } } - - pub fn latch(&mut self) -> QueryLatch<'tcx> { - self.latch.get_or_insert_with(QueryLatch::new).clone() - } - - /// Signals to waiters that the query is complete. - /// - /// This does nothing for single threaded rustc, - /// as there are no concurrent jobs which could be waiting on us - #[inline] - pub fn signal_complete(self) { - if let Some(latch) = self.latch { - latch.set(); - } - } } /// For a particular query and key, tracks the status of a query evaluation @@ -122,67 +107,7 @@ pub struct QueryLatch<'tcx> { } impl<'tcx> QueryLatch<'tcx> { - fn new() -> Self { + pub fn new() -> Self { QueryLatch { waiters: Arc::new(Mutex::new(Some(Vec::new()))) } } - - /// Awaits for the query job to complete. - pub fn wait_on(&self, query: Option, span: Span) -> Result<(), QueryCycle<'tcx>> { - let mut waiters_guard = self.waiters.lock(); - let Some(waiters) = &mut *waiters_guard else { - return Ok(()); // already complete - }; - - let waiter = Arc::new(QueryWaiter { - parent: query, - span, - cycle: Mutex::new(None), - condvar: Condvar::new(), - }); - - // We push the waiter on to the `waiters` list. It can be accessed inside - // the `wait` call below, by 1) the `set` method or 2) by deadlock detection. - // Both of these will remove it from the `waiters` list before resuming - // this thread. - waiters.push(Arc::clone(&waiter)); - - // Awaits the caller on this latch by blocking the current thread. - // If this detects a deadlock and the deadlock handler wants to resume this thread - // we have to be in the `wait` call. This is ensured by the deadlock handler - // getting the self.info lock. - rustc_thread_pool::mark_blocked_and_wait(|| { - waiter.condvar.wait(&mut waiters_guard); - // Release the lock before we potentially block when acquiring jobserver token. - drop(waiters_guard); - }); - - // FIXME: Get rid of this lock. We have ownership of the QueryWaiter - // although another thread may still have a Arc reference so we cannot - // use Arc::get_mut - let mut cycle = waiter.cycle.lock(); - match cycle.take() { - None => Ok(()), - Some(cycle) => Err(cycle), - } - } - - /// Sets the latch and resumes all waiters on it - fn set(&self) { - let mut waiters_guard = self.waiters.lock(); - let waiters = waiters_guard.take().unwrap(); // mark the latch as complete - let registry = rustc_thread_pool::Registry::current(); - for waiter in waiters { - rustc_thread_pool::mark_unblocked(®istry); - waiter.condvar.notify_one(); - } - } - - /// Removes a single waiter from the list of waiters. - /// This is used to break query cycles. - pub fn extract_waiter(&self, waiter: usize) -> Arc> { - let mut waiters_guard = self.waiters.lock(); - let waiters = waiters_guard.as_mut().expect("non-empty waiters vec"); - // Remove the waiter from the list of waiters - waiters.remove(waiter) - } } diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index 1b526e416b38d..c8f2968e4af0c 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start measureme = "12.0.1" +parking_lot = "0.12" rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index d220826670729..ca60614ba97db 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -1,7 +1,9 @@ use std::hash::Hash; use std::mem::ManuallyDrop; use std::num::NonZero; +use std::sync::Arc; +use parking_lot::{Condvar, Mutex}; use rustc_data_structures::hash_table::Entry; use rustc_data_structures::{defer, outline, sharded, sync}; use rustc_errors::FatalError; @@ -10,7 +12,7 @@ use rustc_middle::dep_graph::{ }; use rustc_middle::query::{ ActiveKeyStatus, QueryCache, QueryCycle, QueryJob, QueryJobId, QueryLatch, QueryMode, - QueryState, QueryVTable, + QueryState, QueryVTable, QueryWaiter, }; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::tls::{self, ImplicitCtxt}; @@ -64,6 +66,24 @@ fn handle_cycle<'tcx, C: QueryCache>( } } +/// Signals to waiters that the query is complete. +/// +/// This does nothing for single threaded rustc, as there are no concurrent jobs which could be +/// waiting on us. +#[inline] +fn signal_complete(job: QueryJob<'_>) { + if let Some(latch) = job.latch { + // Set the latch and resume all waiters on it. + let mut waiters_guard = latch.waiters.lock(); + let waiters = waiters_guard.take().unwrap(); // mark the latch as complete + let registry = rustc_thread_pool::Registry::current(); + for waiter in waiters { + rustc_thread_pool::mark_unblocked(®istry); + waiter.condvar.notify_one(); + } + } +} + /// Guard object representing the responsibility to execute a query job and /// mark it as completed. /// @@ -120,7 +140,7 @@ where // Also signal the completion of the job, so waiters will continue execution. match status { - ActiveKeyStatus::Started(job) => job.signal_complete(), + ActiveKeyStatus::Started(job) => signal_complete(job), ActiveKeyStatus::Poisoned => panic!(), } } @@ -155,6 +175,47 @@ fn find_and_handle_cycle<'tcx, C: QueryCache>( (handle_cycle(query, tcx, key, cycle), None) } +/// Awaits for the query job to complete. +fn latch_wait_on<'tcx>( + latch: &QueryLatch<'tcx>, + query: Option, + span: Span, +) -> Result<(), QueryCycle<'tcx>> { + let mut waiters_guard = latch.waiters.lock(); + let Some(waiters) = &mut *waiters_guard else { + return Ok(()); // already complete + }; + + let waiter = Arc::new(QueryWaiter { + parent: query, + span, + cycle: Mutex::new(None), + condvar: Condvar::new(), + }); + + // We push the waiter on to the `waiters` list. It can be accessed inside the `wait` call + // below, by 1) the `signal_complete` function or 2) by deadlock detection. Both of these will + // remove it from the `waiters` list before resuming this thread. + waiters.push(Arc::clone(&waiter)); + + // Awaits the caller on this latch by blocking the current thread. If this detects a deadlock + // and the deadlock handler wants to resume this thread we have to be in the `wait` call. This + // is ensured by the deadlock handler getting the waiters lock. + rustc_thread_pool::mark_blocked_and_wait(|| { + waiter.condvar.wait(&mut waiters_guard); + // Release the lock before we potentially block when acquiring jobserver token. + drop(waiters_guard); + }); + + // FIXME: Get rid of this lock. We have ownership of the QueryWaiter although another thread + // may still have a Arc reference so we cannot use Arc::get_mut. + let mut cycle = waiter.cycle.lock(); + match cycle.take() { + None => Ok(()), + Some(cycle) => Err(cycle), + } +} + #[inline(always)] fn wait_for_query<'tcx, C: QueryCache>( query: &'tcx QueryVTable<'tcx, C>, @@ -171,7 +232,7 @@ fn wait_for_query<'tcx, C: QueryCache>( let query_blocked_prof_timer = tcx.prof.query_blocked(); // With parallel queries we might just have to wait on some other thread. - let result = latch.wait_on(current, span); + let result = latch_wait_on(&latch, current, span); match result { Ok(()) => { @@ -277,7 +338,7 @@ fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( ActiveKeyStatus::Started(job) => { if sync::is_dyn_thread_safe() { // Get the latch out - let latch = job.latch(); + let latch = job.latch.get_or_insert_with(QueryLatch::new).clone(); drop(state_lock); // Only call `wait_for_query` if we're using a Rayon thread pool diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index 54f7a42bffb26..d90f4b6ba60ee 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -199,7 +199,7 @@ pub(crate) fn find_dep_kind_root<'tcx>( } /// The locaton of a resumable waiter. The usize is the index into waiters in the query's latch. -/// We'll use this to remove the waiter using `QueryLatch::extract_waiter` if we're waking it up. +/// We'll use this to remove the waiter if we're waking it up in `find_and_process_cycle`. type ResumableWaiterLocation = (QueryJobId, usize); /// This abstracts over non-resumable waiters which are found in `QueryJob`'s `parent` field @@ -410,8 +410,14 @@ fn find_and_process_cycle<'tcx>( // edge which is resumable / waited using a query latch let (waitee_query, waiter_idx) = resumable.unwrap(); - // Extract the waiter we want to resume - let waiter = job_map.latch_of(waitee_query).unwrap().extract_waiter(waiter_idx); + // Extract the waiter we want to resume. + let waiter = { + let latch = job_map.latch_of(waitee_query).unwrap(); + let mut waiters_guard = latch.waiters.lock(); + let waiters = waiters_guard.as_mut().expect("non-empty waiters vec"); + // Remove the waiter from the list of waiters. + waiters.remove(waiter_idx) + }; // Set the cycle error so it will be picked up when resumed *waiter.cycle.lock() = Some(error); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 43aa2d2041eec..f560810e941bd 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -8,6 +8,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(trim_prefix_suffix))] #![feature(arbitrary_self_types)] #![feature(const_default)] #![feature(const_trait_impl)] @@ -17,7 +18,6 @@ #![feature(iter_intersperse)] #![feature(option_into_flat_iter)] #![feature(rustc_attrs)] -#![feature(trim_prefix_suffix)] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 83a1af895032b..67e63ffcb58ae 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -5,9 +5,11 @@ use rustc_infer::infer::{ InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt, TypeOutlivesConstraint, }; use rustc_macros::extension; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, elaborate}; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, elaborate}; use rustc_span::DUMMY_SP; +use crate::traits::ScrubbedTraitError; use crate::traits::outlives_bounds::InferCtxtExt; #[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)] @@ -90,15 +92,30 @@ pub fn ty_known_to_outlive<'tcx>( id: LocalDefId, param_env: ty::ParamEnv<'tcx>, wf_tys: &FxIndexSet>, - ty: Ty<'tcx>, + ty: Unnormalized<'tcx, Ty<'tcx>>, region: ty::Region<'tcx>, ) -> bool { test_region_obligations(tcx, id, param_env, wf_tys, |infcx| { + // Types in region obligations should be normalized. + let ty = if infcx.next_trait_solver() { + let Ok(ty) = crate::solve::deeply_normalize::<_, ScrubbedTraitError<'tcx>>( + infcx.at(&ObligationCause::dummy_with_span(DUMMY_SP), param_env), + ty, + ) else { + return false; + }; + ty + } else { + ty.skip_norm_wip() + }; + infcx.register_type_outlives_constraint_inner(TypeOutlivesConstraint { sub_region: region, sup_type: ty, origin: SubregionOrigin::RelateParamBound(DUMMY_SP, ty, None), }); + + true }) } @@ -119,6 +136,7 @@ pub fn region_known_to_outlive<'tcx>( region_a, ty::VisibleForLeakCheck::Unreachable, ); + true }) } @@ -130,14 +148,16 @@ pub fn test_region_obligations<'tcx>( id: LocalDefId, param_env: ty::ParamEnv<'tcx>, wf_tys: &FxIndexSet>, - add_constraints: impl FnOnce(&InferCtxt<'tcx>), + add_constraints: impl FnOnce(&InferCtxt<'tcx>) -> bool, ) -> bool { // Unfortunately, we have to use a new `InferCtxt` each call, because // region constraints get added and solved there and we need to test each // call individually. let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - add_constraints(&infcx); + if !add_constraints(&infcx) { + return false; + } let errors = infcx.resolve_regions(id, param_env, wf_tys.iter().copied()); tracing::debug!(?errors, "errors"); diff --git a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs index b3d45f3580239..278ccf46b399b 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs @@ -1,16 +1,44 @@ +use std::debug_assert_matches; + use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::bit_set::DenseBitSet; +use rustc_infer::infer::{SubregionOrigin, TypeOutlivesConstraint}; use rustc_middle::ty::{ self, Flags, ImplTraitInTraitData, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; -use rustc_span::bug; +use rustc_span::{DUMMY_SP, bug}; use crate::infer::outlives::test_type_match; use crate::infer::region_constraints::VerifyIfEq; -use crate::regions::{region_known_to_outlive, ty_known_to_outlive}; +use crate::regions::{region_known_to_outlive, test_region_obligations}; + +/// Given a known `param_env` and a set of well formed types, can we prove that +/// `ty` outlives `region`. +/// +/// Copied from `ty_known_to_outlive` without normalization for `ty` because we +/// don't want trait solving in liveness queries. +fn param_known_to_outlive<'tcx>( + tcx: TyCtxt<'tcx>, + id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxIndexSet>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, +) -> bool { + debug_assert_matches!(ty.kind(), ty::Param(_)); + + test_region_obligations(tcx, id, param_env, wf_tys, |infcx| { + infcx.register_type_outlives_constraint_inner(TypeOutlivesConstraint { + sub_region: region, + sup_type: ty, + origin: SubregionOrigin::RelateParamBound(DUMMY_SP, ty, None), + }); + true + }) +} /// For a given alias type, this returns the set of indices into the identity generic args that /// are relevant for liveness, that can be inferred from outlives bounds on the @@ -48,22 +76,16 @@ pub(crate) fn live_args_for_alias_from_outlives_bounds<'tcx>( let outlives_regions: Vec<_> = bounds .iter() .filter_map(|clause| { - let outlives = clause.as_type_outlives_clause()?; - if let Some(outlives) = outlives.no_bound_vars() - && outlives.0 == alias_ty - { - Some(outlives.1) - } else { - test_type_match::extract_verify_if_eq( - tcx, - &outlives.map_bound(|ty::OutlivesClause(ty, bound)| VerifyIfEq { ty, bound }), - // FIXME(#155345): Region handling should generally only - // deal with rigid aliases, making sure we do so correctly - // everywhere is effort, so we're just using `No` everywhere - // for now. This should change soon. - alias_ty, - ) + let ty::OutlivesClause(ty, region) = clause.as_type_outlives_clause()?.skip_binder(); + if ty != alias_ty { + return None; } + + // Opaques can't have higher-ranked outlives item bounds. Higher-ranked item bounds + // for GATs are instantiated with the GAT identity params, so the alias doesn't + // contain any bound regions. If the region is bound, the alias outlives everything. + // For example: `for<'a> Self::Assoc<'non_bound>: 'a`. + if region.is_bound() { Some(tcx.lifetimes.re_static) } else { Some(region) } }) .collect(); tracing::debug!(?outlives_regions); @@ -235,7 +257,7 @@ pub(crate) fn args_known_to_outlive_opaque_params<'tcx>( ty::GenericArgKind::Const(_) => continue, // Lifetimes should be captured ty::GenericArgKind::Lifetime(_) => continue, - ty::GenericArgKind::Type(t) => ty_known_to_outlive( + ty::GenericArgKind::Type(t) => param_known_to_outlive( tcx, def_id, parent_param_env, @@ -296,7 +318,7 @@ pub(crate) fn args_known_to_outlive_non_opaque_params<'tcx>( region_known_to_outlive(tcx, def_id, param_env, &wf_tys, r, outlived_region) } ty::GenericArgKind::Type(t) => { - ty_known_to_outlive(tcx, def_id, param_env, &wf_tys, t, outlived_region) + param_known_to_outlive(tcx, def_id, param_env, &wf_tys, t, outlived_region) } ty::GenericArgKind::Const(_) => false, }; diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index ceec98c8659fb..6a44f9ef5374e 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -183,6 +183,34 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// /// This method is equivalent to [`DebugStruct::field`], but formats the /// value using a provided closure rather than by calling [`Debug::fmt`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(debug_closure_helpers)] + /// + /// use std::fmt; + /// + /// struct Bar { + /// bar: i32, + /// another: String, + /// } + /// + /// impl fmt::Debug for Bar { + /// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// fmt.debug_struct("Bar") + /// // Print `bar` as a hex value + /// .field_with("bar", |fmt| write!(fmt, "{:#010x}", &self.bar)) + /// .field("another", &self.another) + /// .finish() + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Bar { bar: 10, another: "Hello World".to_string() }), + /// r#"Bar { bar: 0x0000000a, another: "Hello World" }"#, + /// ); + /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] pub fn field_with(&mut self, name: &str, value_fmt: F) -> &mut Self where @@ -376,6 +404,31 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { /// /// This method is equivalent to [`DebugTuple::field`], but formats the /// value using a provided closure rather than by calling [`Debug::fmt`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(debug_closure_helpers)] + /// + /// use std::fmt; + /// + /// struct Foo(i32, String); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// fmt.debug_tuple("Foo") + /// // Print the first field as a hex value + /// .field_with(|fmt| write!(fmt, "{:#010x}", &self.0)) + /// .field(&self.1) + /// .finish() + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Foo(10, "Hello World".to_string())), + /// r#"Foo(0x0000000a, "Hello World")"#, + /// ); + /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] pub fn field_with(&mut self, value_fmt: F) -> &mut Self where @@ -582,6 +635,31 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// /// This method is equivalent to [`DebugSet::entry`], but formats the /// entry using a provided closure rather than by calling [`Debug::fmt`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(debug_closure_helpers)] + /// + /// use std::fmt; + /// + /// struct Foo(Vec, Vec); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// fmt.debug_set() + /// .entry(&self.0) + /// // Print the second member as a set + /// .entry_with(|fmt| fmt.debug_set().entries(&self.1).finish()) + /// .finish() + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Foo(vec![10, 11], vec![12, 13])), + /// "{[10, 11], {12, 13}}", + /// ); + /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] pub fn entry_with(&mut self, entry_fmt: F) -> &mut Self where @@ -774,6 +852,31 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// /// This method is equivalent to [`DebugList::entry`], but formats the /// entry using a provided closure rather than by calling [`Debug::fmt`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(debug_closure_helpers)] + /// + /// use std::fmt; + /// + /// struct Foo(Vec, Vec); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// fmt.debug_list() + /// .entry(&self.0) + /// // Print the second member as a set + /// .entry_with(|fmt| fmt.debug_set().entries(&self.1).finish()) + /// .finish() + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Foo(vec![10, 11], vec![12, 13])), + /// "[[10, 11], {12, 13}]", + /// ); + /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] pub fn entry_with(&mut self, entry_fmt: F) -> &mut Self where @@ -1032,6 +1135,34 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// This method is equivalent to [`DebugMap::key`], but formats the /// key using a provided closure rather than by calling [`Debug::fmt`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(debug_closure_helpers)] + /// + /// use std::fmt; + /// + /// struct Foo(Vec<(String, i32)>); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// let mut map = fmt.debug_map(); + /// for (k, v) in &self.0 { + /// // Append "entry" to each key + /// map.key_with(|fmt| write!(fmt, "entry {k}")); + /// // Write values as hex + /// map.value_with(|fmt| write!(fmt, "{v:#010x}")); + /// } + /// map.finish() + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])), + /// r#"{entry A: 0x0000000a, entry B: 0x0000000b}"#, + /// ); + /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] pub fn key_with(&mut self, key_fmt: F) -> &mut Self where @@ -1097,6 +1228,34 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// This method is equivalent to [`DebugMap::value`], but formats the /// value using a provided closure rather than by calling [`Debug::fmt`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(debug_closure_helpers)] + /// + /// use std::fmt; + /// + /// struct Foo(Vec<(String, i32)>); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// let mut map = fmt.debug_map(); + /// for (k, v) in &self.0 { + /// // Append "entry" to each key + /// map.key_with(|fmt| write!(fmt, "entry {k}")); + /// // Write values as hex + /// map.value_with(|fmt| write!(fmt, "{v:#010x}")); + /// } + /// map.finish() + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])), + /// r#"{entry A: 0x0000000a, entry B: 0x0000000b}"#, + /// ); + /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] pub fn value_with(&mut self, value_fmt: F) -> &mut Self where diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 6efc9e4f28a16..fa4d6d9f90946 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2786,8 +2786,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(trim_prefix_suffix)] - /// /// let v = &[10, 40, 30]; /// /// // Prefix present - removes it @@ -2803,7 +2801,7 @@ impl [T] { /// assert_eq!(b"hello".trim_prefix(prefix.as_bytes()), b"llo".as_ref()); /// ``` #[must_use = "returns the subslice without modifying the original"] - #[unstable(feature = "trim_prefix_suffix", issue = "142312")] + #[stable(feature = "trim_prefix_suffix", since = "CURRENT_RUSTC_VERSION")] pub fn trim_prefix + ?Sized>(&self, prefix: &P) -> &[T] where T: PartialEq, @@ -2829,8 +2827,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(trim_prefix_suffix)] - /// /// let v = &[10, 40, 30]; /// /// // Suffix present - removes it @@ -2843,7 +2839,7 @@ impl [T] { /// assert_eq!(v.trim_suffix(&[50, 30]), &[10, 40, 30][..]); /// ``` #[must_use = "returns the subslice without modifying the original"] - #[unstable(feature = "trim_prefix_suffix", issue = "142312")] + #[stable(feature = "trim_prefix_suffix", since = "CURRENT_RUSTC_VERSION")] pub fn trim_suffix + ?Sized>(&self, suffix: &P) -> &[T] where T: PartialEq, diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index fe58d848e67c2..db52d3bada4c8 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2545,8 +2545,6 @@ impl str { /// # Examples /// /// ``` - /// #![feature(trim_prefix_suffix)] - /// /// // Prefix present - removes it /// assert_eq!("foo:bar".trim_prefix("foo:"), "bar"); /// assert_eq!("foofoo".trim_prefix("foo"), "foo"); @@ -2559,7 +2557,7 @@ impl str { /// ``` #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] - #[unstable(feature = "trim_prefix_suffix", issue = "142312")] + #[stable(feature = "trim_prefix_suffix", since = "CURRENT_RUSTC_VERSION")] pub fn trim_prefix(&self, prefix: P) -> &str { prefix.strip_prefix_of(self).unwrap_or(self) } @@ -2582,8 +2580,6 @@ impl str { /// # Examples /// /// ``` - /// #![feature(trim_prefix_suffix)] - /// /// // Suffix present - removes it /// assert_eq!("bar:foo".trim_suffix(":foo"), "bar"); /// assert_eq!("foofoo".trim_suffix("foo"), "foo"); @@ -2596,7 +2592,7 @@ impl str { /// ``` #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] - #[unstable(feature = "trim_prefix_suffix", issue = "142312")] + #[stable(feature = "trim_prefix_suffix", since = "CURRENT_RUSTC_VERSION")] pub fn trim_suffix(&self, suffix: P) -> &str where for<'a> P::Searcher<'a>: ReverseSearcher<'a>, diff --git a/library/coretests/tests/pattern.rs b/library/coretests/tests/pattern.rs index b5cd019c59e76..2e3475be57ceb 100644 --- a/library/coretests/tests/pattern.rs +++ b/library/coretests/tests/pattern.rs @@ -572,3 +572,103 @@ fn double_ended_regression_test() { next_match => Done ); } + +#[test] +fn two_way_next_reject_skips_matches() { + // `next_reject` must not report the matched regions as rejects + + // plen - pattern len + // ulen - unmatch len + #[track_caller] + fn check_fw_bw<'a, T>(haystack: &'a str, pat: T, plen: usize, ulen: usize) + where + T: Pattern + Copy, + T::Searcher<'a>: ReverseSearcher<'a>, + { + // haystack is a concatenation of 3 fragments: [Match, Reject, Match] + // prepare ranges that point to each fragment + let f1 = (0, plen); + let f2 = (f1.1, f1.1 + ulen); + let f3 = (f2.1, f2.1 + plen); + + // search forward, should match fragments f2 (possibly partially) as a rejection + // and f3 as a match + + let mut searcher = pat.into_searcher(haystack); + // find first forward rejection + let (start, end) = searcher.next_reject().expect( + "inputs are constructed such that the haystack contains a reject in the middle", + ); + assert_eq!(start, f2.0, "forward reject should start at the second fragment (f2)"); + assert!(start < end && end <= f2.1, "fw reject must be a non-empty part of f2"); + assert_eq!( + searcher.next_match(), + Some(f3), + "first fw match after the rejection should point at the entirety of the third fragment (f3)" + ); + assert_eq!(searcher.next_match(), None, "there should be no matches after the f3"); + + // search backwards , should match fragments f2 (possibly partially) as a rejection + // and f1 as a match + + let mut searcher = pat.into_searcher(haystack); + let (start, end) = searcher.next_reject_back().expect( + "inputs are constructed such that the haystack contains a reject in the middle", + ); + assert_eq!(end, f2.1, "forward reject should end at the second fragment (f2)"); + assert!(f2.0 <= start && start < end, "bw reject must be a non-empty part of f2"); + assert_eq!( + searcher.next_match_back(), + Some(f1), + "first bw match after the rejection should point at the entirety of the first fragment (f1)" + ); + assert_eq!(searcher.next_match_back(), None, "there should be no matches after the f1"); + } + + // haystack is always a concatenation of [Match, Reject, Match] + // look for a string slice with several ASCII chars + check_fw_bw("XYZabcXYZ", "XYZ", 3, 3); + + // look for a string slice made of a single utf8 character, 1 to 4 bytes + check_fw_bw("XabcX", "X", 1, 3); + check_fw_bw("\u{00e9}abc\u{00e9}", "\u{00e9}", 2, 3); + check_fw_bw("\u{20ac}abc\u{20ac}", "\u{20ac}", 3, 3); + check_fw_bw("\u{1f60a}abc\u{1f60a}", "\u{1f60a}", 4, 3); + + // look for a single utf8 character, 1 to 4 bytes + check_fw_bw("XabcX", 'X', 1, 3); + check_fw_bw("\u{00e9}abc\u{00e9}", '\u{00e9}', 2, 3); + check_fw_bw("\u{20ac}abc\u{20ac}", '\u{20ac}', 3, 3); + check_fw_bw("\u{1f60a}abc\u{1f60a}", '\u{1f60a}', 4, 3); + + // same, but a singleton set of characters, matches any + check_fw_bw("XabcX", ['X'], 1, 3); + check_fw_bw("\u{00e9}abc\u{00e9}", ['\u{00e9}'], 2, 3); + check_fw_bw("\u{20ac}abc\u{20ac}", ['\u{20ac}'], 3, 3); + check_fw_bw("\u{1f60a}abc\u{1f60a}", ['\u{1f60a}'], 4, 3); + + // same, but match by a closure + check_fw_bw("XabcX", |c| c == 'X', 1, 3); + check_fw_bw("\u{00e9}abc\u{00e9}", |c| c == '\u{00e9}', 2, 3); + check_fw_bw("\u{20ac}abc\u{20ac}", |c| c == '\u{20ac}', 3, 3); + check_fw_bw("\u{1f60a}abc\u{1f60a}", |c| c == '\u{1f60a}', 4, 3); +} + +#[test] +fn two_way_next_reject_never_reports_empty_reject() { + // A reject must never be an empty range + + let mut searcher = "XYZ".into_searcher("XYZ"); + assert_eq!( + searcher.next_reject(), + None, + "Haystack fully covered by the pattern: there are no rejects at all." + ); + + let mut searcher = "XYZ".into_searcher("XYZ"); + assert_eq!( + searcher.next_reject_back(), + None, + "Haystack fully covered by the pattern: there are no rejects at all." + ); +} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5f98aa54a6620..3f853df52bc53 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -390,7 +390,6 @@ #![feature(str_internals)] #![feature(sync_unsafe_cell)] #![feature(temporary_niche_types)] -#![feature(trim_prefix_suffix)] #![feature(ub_checks)] #![feature(uint_carryless_mul)] #![feature(unsafe_pinned)] diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 104c5dba70b68..3e1fe13ee6fc2 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -188,7 +188,7 @@ pub impl(self) trait CommandExt { /// /// /// [1]: - #[unstable(feature = "windows_process_extensions_show_window", issue = "127544")] + #[stable(feature = "windows_process_extensions_show_window", since = "CURRENT_RUSTC_VERSION")] fn show_window(&mut self, cmd_show: u16) -> &mut process::Command; /// Forces all arguments to be wrapped in quote (`"`) characters. @@ -451,14 +451,23 @@ impl CommandExt for process::Command { } } -#[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")] +#[stable( + feature = "windows_process_extensions_main_thread_handle", + since = "CURRENT_RUSTC_VERSION" +)] pub impl(self) trait ChildExt { /// Extracts the main thread raw handle, without taking ownership - #[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")] + #[stable( + feature = "windows_process_extensions_main_thread_handle", + since = "CURRENT_RUSTC_VERSION" + )] fn main_thread_handle(&self) -> BorrowedHandle<'_>; } -#[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")] +#[stable( + feature = "windows_process_extensions_main_thread_handle", + since = "CURRENT_RUSTC_VERSION" +)] impl ChildExt for process::Child { fn main_thread_handle(&self) -> BorrowedHandle<'_> { self.handle.main_thread_handle() diff --git a/library/std/src/path.rs b/library/std/src/path.rs index f06625613ec00..fe72ad4692c02 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2752,7 +2752,6 @@ impl Path { /// # Examples /// /// ``` - /// #![feature(trim_prefix_suffix)] /// use std::path::Path; /// /// let path = Path::new("/test/haha/foo.txt"); @@ -2770,7 +2769,7 @@ impl Path { /// assert_eq!(path.trim_prefix("/haha"), path); /// ``` #[must_use = "this returns the remaining path as a new path, without modifying the original"] - #[unstable(feature = "trim_prefix_suffix", issue = "142312")] + #[stable(feature = "trim_prefix_suffix", since = "CURRENT_RUSTC_VERSION")] pub fn trim_prefix

(&self, base: P) -> &Path where P: AsRef, diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 6414a235698b8..20ea03d36eb0e 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -2206,6 +2206,7 @@ impl crate::error::Error for ExitStatusError {} /// ``` #[derive(Clone, Copy, Debug, PartialEq)] #[stable(feature = "process_exitcode", since = "1.61.0")] +#[must_use] pub struct ExitCode(imp::ExitCode); #[stable(feature = "process_exitcode", since = "1.61.0")] diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 25886d295b3bc..7eb632ba69882 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -18,7 +18,6 @@ #![doc(test(attr(deny(warnings))))] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![feature(exitcode_exit_method)] #![feature(file_buffered)] #![feature(internal_output_capture)] #![feature(io_const_error)] @@ -31,16 +30,17 @@ #![warn(rustdoc::unescaped_backticks)] #![warn(unreachable_pub)] +pub use std::process::ExitCode; // used by rustc-generated test harness + pub use cli::TestOpts; pub use self::ColorConfig::*; pub use self::bench::{Bencher, black_box}; pub use self::console::run_tests_console; pub use self::options::{ColorConfig, Options, OutputFormat, RunIgnored, ShouldPanic}; -pub use self::types::TestName::*; pub use self::types::*; -// Module to be used by rustc to compile tests in libtest +// Make some items publicly available for our own tests. pub mod test { pub use crate::bench::Bencher; pub use crate::cli::{TestOpts, parse_opts}; @@ -48,18 +48,13 @@ pub mod test { pub use crate::options::{Options, RunIgnored, RunStrategy, ShouldPanic}; pub use crate::test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk}; pub use crate::time::{TestExecTime, TestTimeOptions}; - pub use crate::types::{ - DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, - TestDescAndFn, TestId, TestList, TestListOrder, TestName, TestType, - }; - pub use crate::{assert_test_result, filter_tests, run_test, test_main, test_main_env_args}; } use std::collections::VecDeque; use std::io::prelude::Write; use std::mem::ManuallyDrop; use std::panic::{self, AssertUnwindSafe, PanicHookInfo, catch_unwind}; -use std::process::{self, Command, ExitCode, Termination}; +use std::process::{self, Command, Termination}; use std::sync::mpsc::{Sender, channel}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; @@ -170,34 +165,28 @@ fn test_main_inner(args: &[String], tests: TestList<'_>, options: Option ExitCode { // This is supposed to be reasonably fast even in Miri. In particular, when invoked via `--exact // test`, we want the entire invocation to be `O(log n)` in the number of tests: never iterate // the entire test list (as that list could be big)! let args = env::args().collect::>(); // Tests are sorted by name at compile time by mk_tests_slice. let tests = TestList::new(tests, TestListOrder::Sorted); - let exit = test_main_inner(&args, tests, None); - // We do *not* want to exit here on success, that breaks coverage tracking on Windows. - if exit != std::process::ExitCode::SUCCESS { - exit.exit_process(); - } + test_main_inner(&args, tests, None) } -/// A variant that takes the arguments from the command line. Exits the process if there -/// was an error, returns on success. +/// A variant that takes the arguments from the command line. /// /// Runs tests in panic=abort mode, which involves spawning subprocesses for /// tests. If we are invoked as subprocess, this function does not return. /// /// This is the entry point for the main function generated by `rustc --test` /// when panic=abort. -pub fn test_main_env_args_abort(tests: &[&TestDescAndFn]) { +pub fn test_main_env_args_abort(tests: &[&TestDescAndFn]) -> ExitCode { // If we're being run in SpawnedSecondary mode, run the test here. run_test // will then exit the process. if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) { @@ -246,10 +235,7 @@ pub fn test_main_env_args_abort(tests: &[&TestDescAndFn]) { let args = env::args().collect::>(); // Tests are sorted by name at compile time by mk_tests_slice. let tests = TestList::new(tests, TestListOrder::Sorted); - let exit = test_main_inner(&args, tests, Some(Options::new().panic_abort(true))); - if exit != std::process::ExitCode::SUCCESS { - exit.exit_process(); - } + test_main_inner(&args, tests, Some(Options::new().panic_abort(true))) } /// Public API used by rustdoc to display the `total` and `compilation` times in the expected diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 20e992945c917..24427ff5cb3d3 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -1,4 +1,5 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(trim_prefix_suffix))] #![cfg_attr(not(bootstrap), feature(exitcode_exit_method))] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/", @@ -14,7 +15,6 @@ #![feature(iter_partition_in_place)] #![feature(rustc_private)] #![feature(test)] -#![feature(trim_prefix_suffix)] #![feature(variant_count)] #![recursion_limit = "256"] #![warn(rustc::internal)] diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs index 9cd71c62eb32a..dac7a24bf2a8d 100644 --- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs +++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs @@ -65,9 +65,9 @@ impl LateLintPass<'_> for ItemsAfterTestModule { let after: Vec<_> = items .filter(|item| { - // Ignore the generated test main function - if let ItemKind::Fn { ident, .. } = item.kind - && ident.name == sym::main + // Ignore the generated test main function and `extern crate test` + if (matches!(item.kind, ItemKind::Fn { ident, .. } if ident.name == sym::main) + || matches!(item.kind, ItemKind::ExternCrate(None, ident) if ident.name == sym::test)) && item.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::TestHarness) { false diff --git a/tests/assembly-llvm/closure-inherit-target-feature.rs b/tests/assembly-llvm/closure-inherit-target-feature.rs index c3753a534a027..737e4ed655218 100644 --- a/tests/assembly-llvm/closure-inherit-target-feature.rs +++ b/tests/assembly-llvm/closure-inherit-target-feature.rs @@ -8,16 +8,14 @@ use std::arch::x86_64::{__m128, _mm_blend_ps}; -// Use an explicit return pointer to prevent tail call optimization. #[no_mangle] pub unsafe fn sse41_blend_nofeature(x: __m128, y: __m128, ret: *mut __m128) { let f = { // check that _mm_blend_ps is not being inlined into the closure // CHECK-LABEL: {{sse41_blend_nofeature:}} // CHECK-NOT: blendps - // CHECK: {{call .*_mm_blend_ps.*}} + // CHECK: {{(call|jmp) .*_mm_blend_ps.*}} // CHECK-NOT: blendps - // CHECK: ret #[inline(never)] |x, y, ret: *mut __m128| unsafe { *ret = _mm_blend_ps(x, y, 0b0101) } }; diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index beefbdb889940..610a271d91144 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -820,27 +820,28 @@ //@ revisions: riscv64gc_unknown_nuttx_elf //@ [riscv64gc_unknown_nuttx_elf] compile-flags: --target riscv64gc-unknown-nuttx-elf //@ [riscv64gc_unknown_nuttx_elf] needs-llvm-components: riscv -// FIXME: disabled since it requires a custom LLVM until the upstream LLVM adds support for the target (https://github.com/espressif/llvm-project/issues/4) -/* - revisions: xtensa_esp32_none_elf - [xtensa_esp32_none_elf] compile-flags: --target xtensa-esp32-none-elf - [xtensa_esp32_none_elf] needs-llvm-components: xtensa - revisions: xtensa_esp32_espidf - [xtensa_esp32_espidf] compile-flags: --target xtensa-esp32s2-espidf - [xtensa_esp32_espidf] needs-llvm-components: xtensa - revisions: xtensa_esp32s2_none_elf - [xtensa_esp32s2_none_elf] compile-flags: --target xtensa-esp32s2-none-elf - [xtensa_esp32s2_none_elf] needs-llvm-components: xtensa - revisions: xtensa_esp32s2_espidf - [xtensa_esp32s2_espidf] compile-flags: --target xtensa-esp32s2-espidf - [xtensa_esp32s2_espidf] needs-llvm-components: xtensa - revisions: xtensa_esp32s3_none_elf - [xtensa_esp32s3_none_elf] compile-flags: --target xtensa-esp32s3-none-elf - [xtensa_esp32s3_none_elf] needs-llvm-components: xtensa - revisions: xtensa_esp32s3_espidf - [xtensa_esp32s3_espidf] compile-flags: --target xtensa-esp32s3-espidf - [xtensa_esp32s3_espidf] needs-llvm-components: xtensa -*/ +//@ revisions: xtensa_esp32_none_elf +//@ [xtensa_esp32_none_elf] compile-flags: --target xtensa-esp32-none-elf +//@ [xtensa_esp32_none_elf] needs-llvm-components: xtensa +//@ revisions: xtensa_esp32_espidf +//@ [xtensa_esp32_espidf] compile-flags: --target xtensa-esp32s2-espidf +//@ [xtensa_esp32_espidf] needs-llvm-components: xtensa +//@ revisions: xtensa_esp32s2_none_elf +//@ [xtensa_esp32s2_none_elf] compile-flags: --target xtensa-esp32s2-none-elf +//@ [xtensa_esp32s2_none_elf] needs-llvm-components: xtensa +//@ revisions: xtensa_esp32s2_espidf +//@ [xtensa_esp32s2_espidf] compile-flags: --target xtensa-esp32s2-espidf +//@ [xtensa_esp32s2_espidf] needs-llvm-components: xtensa +//@ revisions: xtensa_esp32s3_none_elf +//@ [xtensa_esp32s3_none_elf] compile-flags: --target xtensa-esp32s3-none-elf +//@ [xtensa_esp32s3_none_elf] needs-llvm-components: xtensa +//@ revisions: xtensa_esp32s3_espidf +//@ [xtensa_esp32s3_espidf] compile-flags: --target xtensa-esp32s3-espidf +//@ [xtensa_esp32s3_espidf] needs-llvm-components: xtensa + +// xtensa support requires a more recent LLVM. +//@ min-llvm-version: 22 + // Sanity-check that each target can produce assembly code. #![feature(no_core, lang_items)] diff --git a/tests/pretty/tests-are-sorted.pp b/tests/pretty/tests-are-sorted.pp index 0002189b48c04..e3edb7e22a0b5 100644 --- a/tests/pretty/tests-are-sorted.pp +++ b/tests/pretty/tests-are-sorted.pp @@ -80,10 +80,10 @@ test::assert_test_result(a_test())), }; fn a_test() {} +extern crate test; #[rustc_main] #[coverage(off)] #[doc(hidden)] -pub fn main() -> () { - extern crate test; +pub fn main() -> test::ExitCode { test::test_main_env_args(&[&a_test, &m_test, &z_test]) } diff --git a/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.rs b/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.rs new file mode 100644 index 0000000000000..98fed192ed54b --- /dev/null +++ b/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Zassumptions-on-binders +//@ needs-rustc-debug-assertions +//@ normalize-stderr: "(\n)\n$" -> "$1" + +// Regression test for #161067. A nested non-rigid alias must be normalized +// before it reaches lexical region solving through `ty_known_to_outlive`. + +struct D; + +trait Des { + type Out<'x, T>; + //~^ ERROR missing required bound on `Out` + + fn des<'z>() -> Self::Out<'z, Self::Out<'z, D>>; +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.stderr b/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.stderr new file mode 100644 index 0000000000000..d73c0f6f1fbeb --- /dev/null +++ b/tests/ui/assumptions_on_binders/nested-gat-outlives-issue-161067.stderr @@ -0,0 +1,12 @@ +error: missing required bound on `Out` + --> $DIR/nested-gat-outlives-issue-161067.rs:11:5 + | +LL | type Out<'x, T>; + | ^^^^^^^^^^^^^^^- + | | + | help: add the required where clause: `where T: 'x` + | + = note: this bound is currently required to ensure that impls have maximum flexibility + = note: we are soliciting feedback, see issue #87479 for more information + +error: aborting due to 1 previous error diff --git a/tests/ui/attributes/reexport-test-harness-entry-point.rs b/tests/ui/attributes/reexport-test-harness-entry-point.rs index 4de308dafe4a7..ca942e90b04d4 100644 --- a/tests/ui/attributes/reexport-test-harness-entry-point.rs +++ b/tests/ui/attributes/reexport-test-harness-entry-point.rs @@ -11,5 +11,5 @@ fn _unused() { // should resolve to the entry point function the --test harness // creates. - test_main(); + let _ = test_main(); } diff --git a/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.legacy.stderr b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.legacy.stderr new file mode 100644 index 0000000000000..8cf41b45af61d --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.legacy.stderr @@ -0,0 +1,22 @@ +error[E0505]: cannot move out of `b` because it is borrowed + --> $DIR/drop-liveness-invariance-issue-160670.rs:38:10 + | +LL | let b = Box::new(0u8); + | - binding `b` declared here +LL | let d; +LL | d = mk(&*b); + | --- borrow of `*b` occurs here +LL | drop(b); + | ^ move out of `b` occurs here +LL | } + | - borrow might be used here, when `d` is dropped and runs the `Drop` code for type `D` + | +help: consider cloning the value if the performance cost is acceptable + | +LL - d = mk(&*b); +LL + d = mk(&b.clone()); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.nll.stderr b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.nll.stderr new file mode 100644 index 0000000000000..8cf41b45af61d --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.nll.stderr @@ -0,0 +1,22 @@ +error[E0505]: cannot move out of `b` because it is borrowed + --> $DIR/drop-liveness-invariance-issue-160670.rs:38:10 + | +LL | let b = Box::new(0u8); + | - binding `b` declared here +LL | let d; +LL | d = mk(&*b); + | --- borrow of `*b` occurs here +LL | drop(b); + | ^ move out of `b` occurs here +LL | } + | - borrow might be used here, when `d` is dropped and runs the `Drop` code for type `D` + | +help: consider cloning the value if the performance cost is acceptable + | +LL - d = mk(&*b); +LL + d = mk(&b.clone()); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.polonius.stderr b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.polonius.stderr new file mode 100644 index 0000000000000..8cf41b45af61d --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.polonius.stderr @@ -0,0 +1,22 @@ +error[E0505]: cannot move out of `b` because it is borrowed + --> $DIR/drop-liveness-invariance-issue-160670.rs:38:10 + | +LL | let b = Box::new(0u8); + | - binding `b` declared here +LL | let d; +LL | d = mk(&*b); + | --- borrow of `*b` occurs here +LL | drop(b); + | ^ move out of `b` occurs here +LL | } + | - borrow might be used here, when `d` is dropped and runs the `Drop` code for type `D` + | +help: consider cloning the value if the performance cost is acceptable + | +LL - d = mk(&*b); +LL + d = mk(&b.clone()); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.rs b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.rs new file mode 100644 index 0000000000000..b1f53190b2c74 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.rs @@ -0,0 +1,39 @@ +// From https://github.com/rust-lang/rust/issues/160670 This issue was +// discovered when developing Polonius alpha. A live local (the one holding the +// struct `D`) had its type be drop-live, but only partially. This had not +// previously triggered any issues because it did not affect region liveness, +// but it did affect Polonius' region variance computations, since the outer `D` +// nesting was removed to obtain `fn(&'a T)`, which unlike the associated type +// isn't invariant. +// +// The split declaration/assignment on lines 39--40 is load bearing; without +// them the bug does not appear due to a `FakeRead` being introduced and +// ensuring liveness. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius legacy +//@ [nll] compile-flags: -Z polonius=off +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy + +struct D(T::Arg); + +trait HasArg { + type Arg; +} +impl<'a, T> HasArg for fn(&'a T) { + type Arg = &'a T; +} +impl Drop for D { + fn drop(&mut self) {} +} +fn mk<'a, T>(r: &'a T) -> D { + D(r) +} + +fn main() { + let b = Box::new(0u8); + let d; + d = mk(&*b); + drop(b); //~ ERROR cannot move out of `b` because it is borrowed +} diff --git a/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.rs b/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.rs index 5656d55d9f9c6..f2c8a242dbcc0 100644 --- a/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.rs +++ b/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.rs @@ -28,13 +28,13 @@ fn baz::LEN]>() {} fn main() { foo::(); - //~^ ERROR: expected array with 2 elements, found 0 elements + //~^ ERROR: the constant `*b""` is not of type `[u8; 2]` foo::(); - //~^ ERROR: expected array with 2 elements, found 3 elements + //~^ ERROR: the constant `*b"\x00\x00\x00"` is not of type `[u8; 2]` bar::<{ [] }>(); - //~^ ERROR: expected array with 2 elements, found 0 elements + //~^ ERROR: the constant `*b""` is not of type `[u8; 2]` bar::<{ [1, 2, 3] }>(); - //~^ ERROR: expected array with 2 elements, found 3 elements + //~^ ERROR: the constant `*b"\x01\x02\x03"` is not of type `[u8; 2]` baz::<{ [42] }>(); - //~^ ERROR: expected array with 3 elements, found 1 elements + //~^ ERROR: the constant `*b"*"` is not of type `[u8; 3]` } diff --git a/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.stderr b/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.stderr index e83b0e98dfe6c..82a9cfc86b5d7 100644 --- a/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.stderr +++ b/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.stderr @@ -1,32 +1,62 @@ -error: expected array with 2 elements, found 0 elements - --> $DIR/array-const-arg-len-mismatch.rs:30:20 +error: the constant `*b""` is not of type `[u8; 2]` + --> $DIR/array-const-arg-len-mismatch.rs:30:11 | LL | foo::(); - | ^^ + | ^^ expected `[u8; 2]`, found `[u8; 0]` + | +note: required by a const generic parameter in `foo` + --> $DIR/array-const-arg-len-mismatch.rs:11:42 + | +LL | fn foo() -> [T; N] { + | ^^^^^^^^^^^^^^^ required by this const generic parameter in `foo` -error: expected array with 2 elements, found 3 elements - --> $DIR/array-const-arg-len-mismatch.rs:32:20 +error: the constant `*b"\x00\x00\x00"` is not of type `[u8; 2]` + --> $DIR/array-const-arg-len-mismatch.rs:32:11 | LL | foo::(); - | ^^^^^^^^^ + | ^^ expected `[u8; 2]`, found `[u8; 3]` + | +note: required by a const generic parameter in `foo` + --> $DIR/array-const-arg-len-mismatch.rs:11:42 + | +LL | fn foo() -> [T; N] { + | ^^^^^^^^^^^^^^^ required by this const generic parameter in `foo` -error: expected array with 2 elements, found 0 elements +error: the constant `*b""` is not of type `[u8; 2]` --> $DIR/array-const-arg-len-mismatch.rs:34:13 | LL | bar::<{ [] }>(); - | ^^ + | ^^ expected `[u8; 2]`, found `[u8; 0]` + | +note: required by a const generic parameter in `bar` + --> $DIR/array-const-arg-len-mismatch.rs:15:8 + | +LL | fn bar() {} + | ^^^^^^^^^^^^^^^^ required by this const generic parameter in `bar` -error: expected array with 2 elements, found 3 elements +error: the constant `*b"\x01\x02\x03"` is not of type `[u8; 2]` --> $DIR/array-const-arg-len-mismatch.rs:36:13 | LL | bar::<{ [1, 2, 3] }>(); - | ^^^^^^^^^ + | ^^^^^^^^^ expected `[u8; 2]`, found `[u8; 3]` + | +note: required by a const generic parameter in `bar` + --> $DIR/array-const-arg-len-mismatch.rs:15:8 + | +LL | fn bar() {} + | ^^^^^^^^^^^^^^^^ required by this const generic parameter in `bar` -error: expected array with 3 elements, found 1 elements +error: the constant `*b"*"` is not of type `[u8; 3]` --> $DIR/array-const-arg-len-mismatch.rs:38:13 | LL | baz::<{ [42] }>(); - | ^^^^ + | ^^^^ expected `[u8; 3]`, found `[u8; 1]` + | +note: required by a const generic parameter in `baz` + --> $DIR/array-const-arg-len-mismatch.rs:27:8 + | +LL | fn baz::LEN]>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this const generic parameter in `baz` error: aborting due to 5 previous errors diff --git a/tests/crashes/160553.rs b/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.rs similarity index 79% rename from tests/crashes/160553.rs rename to tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.rs index dc5628cf598fb..2c680a37ea1f9 100644 --- a/tests/crashes/160553.rs +++ b/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.rs @@ -1,5 +1,4 @@ -//@ known-bug: #160553 -//@ compile-flags: -Copt-level=0 +//! Regression test for #160553 (used to ICE) #![allow(incomplete_features)] #![feature(adt_const_params, min_generic_const_args, macroless_generic_const_args)] #![feature(generic_const_parameter_types)] @@ -20,6 +19,7 @@ fn foo::LEN]>() -> [u8; ::LEN] fn bar() -> [u8; ::LEN] { foo::() + //~^ ERROR the constant `*b"\x01\x02\x03"` is not of type `[u8; ::LEN]` } fn main() { diff --git a/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.stderr b/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.stderr new file mode 100644 index 0000000000000..c9ee69146f326 --- /dev/null +++ b/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.stderr @@ -0,0 +1,14 @@ +error: the constant `*b"\x01\x02\x03"` is not of type `[u8; ::LEN]` + --> $DIR/generic_const_items-mismatched-array-len.rs:21:11 + | +LL | foo::() + | ^ expected `[u8; ::LEN]`, found `[u8; 3]` + | +note: required by a const generic parameter in `foo` + --> $DIR/generic_const_items-mismatched-array-len.rs:16:18 + | +LL | fn foo::LEN]>() -> [u8; ::LEN] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this const generic parameter in `foo` + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/mgca/generic_const_parameter_types-inferred-array-len.rs b/tests/ui/const-generics/mgca/generic_const_parameter_types-inferred-array-len.rs new file mode 100644 index 0000000000000..5891e66aa77b0 --- /dev/null +++ b/tests/ui/const-generics/mgca/generic_const_parameter_types-inferred-array-len.rs @@ -0,0 +1,8 @@ +//@ check-pass + +#![feature(min_adt_const_params, min_generic_const_args, generic_const_parameter_types)] +fn foo() {} + +fn main() { + foo::<_, core::direct_const_arg!([0, 1, 2, 3])>(); +} diff --git a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.gate.stderr b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.gate.stderr deleted file mode 100644 index a236198f4abc9..0000000000000 --- a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.gate.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: anonymous constants referencing generics are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:8:77 - | -LL | const FOO: [T; 0] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ - -error: anonymous constants referencing generics are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:11:62 - | -LL | const BAR: [(); N] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ - -error: anonymous constants with lifetimes in their type are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:14:54 - | -LL | const BAZ<'a>: [&'a (); 0] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ - -error: anonymous constants referencing generics are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:30:83 - | -LL | const ASSOC: [T; 0] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ - -error: anonymous constants referencing generics are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:33:74 - | -LL | const ASSOC_CONST: [(); N] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ - -error: anonymous constants with lifetimes in their type are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:36:63 - | -LL | const ASSOC_LT<'a>: [&'a (); 0] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ - -error: aborting due to 6 previous errors - diff --git a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.nogate.stderr b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.nogate.stderr index a236198f4abc9..03251f4c7bd2b 100644 --- a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.nogate.stderr +++ b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.nogate.stderr @@ -1,38 +1,57 @@ -error: anonymous constants referencing generics are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:8:77 +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/type_const-generic-param-in-type.rs:12:1 | -LL | const FOO: [T; 0] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ +LL | const FOO: [T; 0] = core::direct_const_arg!([]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[T; 0]` must not depend on other generic parameter -error: anonymous constants referencing generics are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:11:62 +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/type_const-generic-param-in-type.rs:15:1 | -LL | const BAR: [(); N] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ +LL | const BAR: StructWithConstParam = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `StructWithConstParam` must not depend on other generic parameter -error: anonymous constants with lifetimes in their type are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:14:54 +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/type_const-generic-param-in-type.rs:19:1 | -LL | const BAZ<'a>: [&'a (); 0] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ +LL | const BAZ<'a>: [&'a (); 0] = core::direct_const_arg!([]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[&'a (); 0]` must not depend on other generic parameter -error: anonymous constants referencing generics are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:30:83 +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/type_const-generic-param-in-type.rs:37:5 | -LL | const ASSOC: [T; 0] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ +LL | const ASSOC: [T; 0] = core::direct_const_arg!([]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[T; 0]` must not depend on other generic parameter -error: anonymous constants referencing generics are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:33:74 +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/type_const-generic-param-in-type.rs:40:5 | -LL | const ASSOC_CONST: [(); N] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ +LL | const ASSOC_CONST: StructWithConstParam = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `StructWithConstParam` must not depend on other generic parameter -error: anonymous constants with lifetimes in their type are not yet supported - --> $DIR/type_const-generic-param-in-type.rs:36:63 +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/type_const-generic-param-in-type.rs:44:5 | -LL | const ASSOC_LT<'a>: [&'a (); 0] = core::direct_const_arg!(const { [] }); - | ^^^^^^^^^^^^ +LL | const ASSOC_LT<'a>: [&'a (); 0] = core::direct_const_arg!([]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[&'a (); 0]` must not depend on other generic parameter -error: aborting due to 6 previous errors +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/type_const-generic-param-in-type.rs:24:5 + | +LL | const ASSOC: [T; 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[T; 0]` must not depend on other generic parameter + +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/type_const-generic-param-in-type.rs:28:5 + | +LL | const ASSOC_CONST: StructWithConstParam; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `StructWithConstParam` must not depend on other generic parameter + +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/type_const-generic-param-in-type.rs:32:5 + | +LL | const ASSOC_LT<'a>: [&'a (); 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[&'a (); 0]` must not depend on other generic parameter + +error: aborting due to 9 previous errors +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.rs b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.rs index e6cf2fd60eb0a..e5e36b79eca57 100644 --- a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.rs +++ b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.rs @@ -1,40 +1,48 @@ //@ revisions: nogate gate -//@ [gate] check-fail -// FIXME(generic_const_parameter_types): this should pass +//@ [gate] check-pass #![expect(incomplete_features)] #![feature(adt_const_params, unsized_const_params, min_generic_const_args, generic_const_items)] #![cfg_attr(gate, feature(generic_const_parameter_types))] -const FOO: [T; 0] = core::direct_const_arg!(const { [] }); -//~^ ERROR anonymous constants referencing generics are not yet supported +use std::marker::ConstParamTy; -const BAR: [(); N] = core::direct_const_arg!(const { [] }); -//~^ ERROR anonymous constants referencing generics are not yet supported +#[derive(ConstParamTy, PartialEq, Eq, Debug)] +struct StructWithConstParam; -const BAZ<'a>: [&'a (); 0] = core::direct_const_arg!(const { [] }); -//~^ ERROR anonymous constants with lifetimes in their type are not yet supported +const FOO: [T; 0] = core::direct_const_arg!([]); +//[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters + +const BAR: StructWithConstParam = + //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters + core::direct_const_arg!(StructWithConstParam::); + +const BAZ<'a>: [&'a (); 0] = core::direct_const_arg!([]); +//[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters trait Tr { - // FIXME(min_generic_const_args): These should error under [nogate] #[rustc_always_gca] const ASSOC: [T; 0]; + //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters #[rustc_always_gca] - const ASSOC_CONST: [(); N]; + const ASSOC_CONST: StructWithConstParam; + //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters #[rustc_always_gca] const ASSOC_LT<'a>: [&'a (); 0]; + //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters } impl Tr for () { - const ASSOC: [T; 0] = core::direct_const_arg!(const { [] }); - //~^ ERROR anonymous constants referencing generics are not yet supported + const ASSOC: [T; 0] = core::direct_const_arg!([]); + //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters - const ASSOC_CONST: [(); N] = core::direct_const_arg!(const { [] }); - //~^ ERROR anonymous constants referencing generics are not yet supported + const ASSOC_CONST: StructWithConstParam = + //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters + core::direct_const_arg!(StructWithConstParam::); - const ASSOC_LT<'a>: [&'a (); 0] = core::direct_const_arg!(const { [] }); - //~^ ERROR anonymous constants with lifetimes in their type are not yet supported + const ASSOC_LT<'a>: [&'a (); 0] = core::direct_const_arg!([]); + //[nogate]~^ ERROR the type of const parameters must not depend on other generic parameters } fn main() {} diff --git a/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.rs b/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.rs index d321f8ce802c0..31bca180df8a8 100644 --- a/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.rs +++ b/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.rs @@ -11,7 +11,6 @@ impl Pins for NoPin {} pub trait PinA { #[rustc_always_gca] const A: &'static () = core::direct_const_arg!(const { &() }); - //~^ ERROR anonymous constants with lifetimes in their type are not yet supported } pub trait Pins {} @@ -19,8 +18,7 @@ pub trait Pins {} impl Pins for T //~^ ERROR conflicting implementations of trait `Pins<_>` for type `NoPin` where - T: PinA, - //~^ ERROR anonymous constants with lifetimes in their type are not yet supported + T: PinA { } diff --git a/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.stderr b/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.stderr index 5ccca6bb0c677..515ee3f0af8be 100644 --- a/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.stderr +++ b/tests/ui/generic-const-items/assoc-const-no-infer-ice-115806.stderr @@ -1,11 +1,5 @@ -error: anonymous constants with lifetimes in their type are not yet supported - --> $DIR/assoc-const-no-infer-ice-115806.rs:22:50 - | -LL | T: PinA, - | ^^^^^^^^^^^^^ - error[E0119]: conflicting implementations of trait `Pins<_>` for type `NoPin` - --> $DIR/assoc-const-no-infer-ice-115806.rs:19:1 + --> $DIR/assoc-const-no-infer-ice-115806.rs:18:1 | LL | impl Pins for NoPin {} | --------------------------- first implementation here @@ -13,17 +7,11 @@ LL | impl Pins for NoPin {} LL | / impl Pins for T LL | | LL | | where -LL | | T: PinA, - | |___________________________________________________________________^ conflicting implementation for `NoPin` +LL | | T: PinA + | |__________________________________________________________________^ conflicting implementation for `NoPin` | = note: downstream crates may implement trait `PinA<_>` for type `NoPin` -error: anonymous constants with lifetimes in their type are not yet supported - --> $DIR/assoc-const-no-infer-ice-115806.rs:13:52 - | -LL | const A: &'static () = core::direct_const_arg!(const { &() }); - | ^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/next-solver/rigid-alias-liveness-issue-160206.rs b/tests/ui/traits/next-solver/rigid-alias-liveness-issue-160206.rs new file mode 100644 index 0000000000000..4899645e10d58 --- /dev/null +++ b/tests/ui/traits/next-solver/rigid-alias-liveness-issue-160206.rs @@ -0,0 +1,27 @@ +//@ compile-flags: -Znext-solver=globally +//@ check-pass + +trait Foo<'x> { + type Out; + fn foo(self) -> Self::Out; +} + +struct Bar; + +impl<'x> Foo<'x> for Bar { + type Out = (); + + fn foo(self) -> Self::Out { + todo!() + } +} + +fn make_static_foo<'x>(_: &'x ()) -> impl Foo<'x, Out: 'static> { + Bar +} + +fn test() { + make_static_foo(&()); +} + +fn main() {}