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_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 3b3a58697b205..54dede083f595 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -253,8 +253,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { } /// If this method returns `true`, then this type should always have a `PassMode` of - /// `Indirect { on_stack: false, .. }` when being used as the argument type of a function with a - /// non-Rustic ABI (this is true for structs annotated with the + /// `Indirect { mode: IndirectMode::Pointer, .. }` when being used as the argument type of a + /// function with a non-Rustic ABI (this is true for structs annotated with the /// `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute). /// /// This is used to replicate some of the behaviour of C array-to-pointer decay; however unlike diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index b056fdc73d40b..79c0309c60aa3 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1772,6 +1772,10 @@ pub struct AddressSpace(pub u32); impl AddressSpace { /// LLVM's `0` address space. pub const ZERO: Self = AddressSpace(0); + /// The address space for constant memory on nvptx and amdgpu. + /// This address space is used e.g. for kernel arguments that are constant throughout the + /// execution. + pub const GPU_CONSTANT: Self = AddressSpace(4); /// The address space for workgroup memory on nvptx and amdgpu. /// See e.g. the `gpu_launch_sized_workgroup_mem` intrinsic for details. pub const GPU_WORKGROUP: Self = AddressSpace(3); diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index b78606058e5e0..f4b2ceb850666 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1201,17 +1201,15 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { } // Point at all the loops that are between this move and the parent item. for span in loop_spans { - spans.push_span_label(sm.guess_head_span(span), ""); + spans.push_span_context(sm.guess_head_span(span)); } // note: verify that your loop breaking logic is correct // --> $DIR/nested-loop-moved-value-wrong-continue.rs:41:17 // | // 28 | for foo in foos { - // | --------------- // ... // 33 | for bar in &bars { - // | ---------------- // ... // 41 | continue; // | ^^^^^^^^ this `continue` advances the loop at line 33 diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index f28fce737c129..7a614db25d668 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1346,7 +1346,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { kind: hir::ImplItemKind::Fn(sig, _), .. }) => { - err.span_label(ident.span, ""); + err.span_context(ident.span); err.span_label( sig.decl.output.span(), "change this to return `FnMut` instead of `Fn`", 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 d050979a1d189..61aa30aa3917c 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -6,8 +6,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}; @@ -554,7 +553,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` @@ -595,6 +595,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 { @@ -609,10 +612,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); @@ -626,20 +644,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_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 1c552ca1a9c32..48ffc43c5cfa1 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -3,7 +3,7 @@ use cranelift_codegen::ir::ArgumentPurpose; use rustc_abi::{Reg, RegKind}; use rustc_target::callconv::{ - ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, PassMode, + ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, IndirectMode, PassMode, }; use smallvec::{SmallVec, smallvec}; @@ -126,8 +126,12 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { assert_eq!(pad_i32_count, 0, "padding support not yet implemented"); cast_target_to_abi_params(cast).into_iter().map(|(_, param)| param).collect() } - PassMode::Indirect { attrs, meta_attrs: None, on_stack } => { - if on_stack { + PassMode::Indirect { attrs, meta_attrs: None, address_space: _, mode } => { + assert!( + mode != IndirectMode::AmdgpuKernelArg, + "unsupported amdgpu kernel argument" + ); + if mode == IndirectMode::OnStack { // Abi requires aligning struct size to pointer size let size = self.layout.size.align_to(tcx.data_layout.pointer_align().abi); let size = u32::try_from(size.bytes()).unwrap(); @@ -139,8 +143,8 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { smallvec![apply_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs)] } } - PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => { - assert!(!on_stack); + PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), address_space: _, mode } => { + assert!(mode == IndirectMode::Pointer); smallvec![ apply_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs), apply_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), meta_attrs), @@ -184,8 +188,8 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { None, cast_target_to_abi_params(cast).into_iter().map(|(_, param)| param).collect(), ), - PassMode::Indirect { attrs, meta_attrs: None, on_stack } => { - assert!(!on_stack); + PassMode::Indirect { attrs, meta_attrs: None, address_space: _, mode } => { + assert!(mode == IndirectMode::Pointer); ( Some(apply_attrs_to_abi_param( AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructReturn), @@ -194,7 +198,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { vec![], ) } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { unreachable!("unsized return value") } } @@ -324,7 +328,7 @@ pub(super) fn cvalue_for_param<'tcx>( PassMode::Cast { ref cast, .. } => { from_casted_value(fx, &block_params, arg_abi.layout, cast) } - PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs, meta_attrs: None, address_space: _, mode: _ } => { assert_eq!(block_params.len(), 1, "{:?}", block_params); if let Some(pointee_align) = attrs.pointee_align && pointee_align < arg_abi.layout.align.abi @@ -342,7 +346,7 @@ pub(super) fn cvalue_for_param<'tcx>( CValue::by_ref(Pointer::new(block_params[0]), arg_abi.layout) } } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { assert_eq!(block_params.len(), 2, "{:?}", block_params); CValue::by_ref_unsized(Pointer::new(block_params[0]), block_params[1], arg_abi.layout) } diff --git a/compiler/rustc_codegen_cranelift/src/abi/returning.rs b/compiler/rustc_codegen_cranelift/src/abi/returning.rs index 36087f96dd776..7f4ee9435b506 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/returning.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/returning.rs @@ -17,12 +17,12 @@ pub(super) fn codegen_return_param<'tcx>( let is_ssa = ssa_analyzed[RETURN_PLACE].is_ssa(fx, fx.fn_abi.ret.layout.ty); (super::make_local_place(fx, RETURN_PLACE, fx.fn_abi.ret.layout, is_ssa), smallvec![]) } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } => { let ret_param = block_params_iter.next().unwrap(); assert_eq!(fx.bcx.func.dfg.value_type(ret_param), fx.pointer_type); (CPlace::for_ptr(Pointer::new(ret_param), fx.fn_abi.ret.layout), smallvec![ret_param]) } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { unreachable!("unsized return value") } }; @@ -50,7 +50,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( ) { let (ret_temp_place, return_ptr) = match ret_arg_abi.mode { PassMode::Ignore => (None, None), - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } => { if let Some(ret_ptr) = ret_place.try_to_ptr() { // This is an optimization to prevent unnecessary copies of the return value when // the return place is already a memory place as opposed to a register. @@ -61,7 +61,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( (Some(place), Some(place.to_ptr().get_addr(fx))) } } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { unreachable!("unsized return value") } PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast { .. } => (None, None), @@ -86,14 +86,14 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( super::pass_mode::from_casted_value(fx, &results, ret_place.layout(), cast); ret_place.write_cvalue(fx, result); } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } => { if let Some(ret_temp_place) = ret_temp_place { // If ret_temp_place is None, it is not necessary to copy the return value. let ret_temp_value = ret_temp_place.to_cvalue(fx); ret_place.write_cvalue(fx, ret_temp_value); } } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { unreachable!("unsized return value") } } @@ -102,10 +102,11 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( /// Codegen a return instruction with the right return value(s) if any. pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, '_>) { match fx.fn_abi.ret.mode { - PassMode::Ignore | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { + PassMode::Ignore + | PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } => { fx.bcx.ins().return_(&[]); } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { unreachable!("unsized return value") } PassMode::Direct(_) => { diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 6a05f1cbbeef1..b5834ca57ebe1 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] use rustc_session::{Session, config}; use rustc_span::bug; -use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; +use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, IndirectMode, PassMode}; #[cfg(feature = "master")] use rustc_target::spec::Arch; @@ -178,19 +178,42 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { let ty = cast.gcc_type(cx); apply_attrs(ty, &cast.attrs, argument_tys.len()) } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: true } => { + PassMode::Indirect { + attrs: _, + meta_attrs: None, + address_space: _, + mode: IndirectMode::OnStack, + } => { // This is a "byval" argument, so we don't apply the `restrict` attribute on it. on_stack_param_indices.insert(argument_tys.len()); arg.layout.gcc_type(cx) } + PassMode::Indirect { + attrs: _, + meta_attrs: None, + address_space: _, + mode: IndirectMode::AmdgpuKernelArg, + } => { + unimplemented!("unsupported amdgpu kernel argument") + } PassMode::Direct(attrs) => { apply_attrs(arg.layout.immediate_gcc_type(cx), &attrs, argument_tys.len()) } - PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => { + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::Pointer, + } => { apply_attrs(cx.type_ptr_to(arg.layout.gcc_type(cx)), &attrs, argument_tys.len()) } - PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => { - assert!(!on_stack); + PassMode::Indirect { + attrs, + meta_attrs: Some(meta_attrs), + address_space: _, + mode, + } => { + assert!(mode == IndirectMode::Pointer); // Construct the type of a (wide) pointer to `ty`, and pass its two fields. // Any two ABI-compatible unsized types have the same metadata type and // moreover the same metadata value leads to the same dynamic size and diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index a45138849e4e0..703986fdab3fa 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -15,7 +15,7 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_session::{Session, config}; use rustc_span::bug; use rustc_target::callconv::{ - ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode, + ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, IndirectMode, PassMode, }; use rustc_target::spec::{Arch, SanitizerSet}; use smallvec::SmallVec; @@ -242,12 +242,12 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { match &self.mode { PassMode::Ignore => {} // Sized indirect arguments - PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs, meta_attrs: None, address_space: _, mode: _ } => { let align = attrs.pointee_align.unwrap_or(self.layout.align.abi); OperandValue::Ref(PlaceValue::new_sized(val, align)).store(bx, dst); } // Unsized indirect arguments cannot be stored - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Cast { cast, pad_i32_count: _ } => { @@ -303,11 +303,11 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { PassMode::Pair(..) => { OperandValue::Pair(next(), next()).store(bx, dst); } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Direct(_) - | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } + | PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } | PassMode::Cast { .. } => { let next_arg = next(); self.store(bx, next_arg, dst); @@ -368,8 +368,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Ignore => cx.type_void(), PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx), PassMode::Cast { cast, pad_i32_count: _ } => cast.llvm_type(cx), - PassMode::Indirect { .. } => { - llargument_tys.push(cx.type_ptr()); + PassMode::Indirect { address_space, .. } => { + let ty = if let Some(address_space) = address_space { + cx.type_ptr_ext(*address_space) + } else { + cx.type_ptr() + }; + llargument_tys.push(ty); cx.type_void() } }; @@ -394,7 +399,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true)); continue; } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { // Construct the type of a (wide) pointer to `ty`, and pass its two fields. // Any two ABI-compatible unsized types have the same metadata type and // moreover the same metadata value leads to the same dynamic size and @@ -405,7 +410,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true)); continue; } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => cx.type_ptr(), + PassMode::Indirect { attrs: _, meta_attrs: None, address_space, mode: _ } => { + if let Some(address_space) = address_space { + cx.type_ptr_ext(*address_space) + } else { + cx.type_ptr() + } + } PassMode::Cast { cast, pad_i32_count } => { // Add padding. llargument_tys.extend(std::iter::repeat_n( @@ -495,8 +506,8 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { apply_range_attr(llvm::AttributePlace::ReturnValue, scalar); } } - PassMode::Indirect { attrs, meta_attrs: _, on_stack } => { - assert!(!on_stack); + PassMode::Indirect { attrs, meta_attrs: _, address_space: _, mode } => { + assert!(*mode == IndirectMode::Pointer); let i = apply(attrs); let sret = llvm::CreateStructRetAttr( cx.llcx, @@ -522,7 +533,12 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { for arg in self.args.iter() { match &arg.mode { PassMode::Ignore => {} - PassMode::Indirect { attrs, meta_attrs: None, on_stack: true } => { + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::OnStack, + } => { let i = apply(attrs); let byval = llvm::CreateByValAttr( cx.llcx, @@ -530,13 +546,31 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { ); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[byval]); } + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::AmdgpuKernelArg, + } => { + let i = apply(attrs); + let byref = llvm::CreateByRefAttr( + cx.llcx, + cx.type_array(cx.type_i8(), arg.layout.size.bytes()), + ); + attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[byref]); + } PassMode::Direct(attrs) => { let i = apply(attrs); if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { apply_range_attr(llvm::AttributePlace::Argument(i), scalar); } } - PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => { + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::Pointer, + } => { let i = apply(attrs); if cx.sess().opts.optimize != config::OptLevel::No { attributes::apply_to_llfn( @@ -546,8 +580,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { ); } } - PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => { - assert!(!on_stack); + PassMode::Indirect { + attrs, + meta_attrs: Some(meta_attrs), + address_space: _, + mode, + } => { + assert!(*mode == IndirectMode::Pointer); apply(attrs); apply(meta_attrs); } @@ -625,8 +664,8 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Direct(attrs) => { attrs.apply_attrs_to_callsite(llvm::AttributePlace::ReturnValue, bx.cx, callsite); } - PassMode::Indirect { attrs, meta_attrs: _, on_stack } => { - assert!(!on_stack); + PassMode::Indirect { attrs, meta_attrs: _, address_space: _, mode } => { + assert!(*mode == IndirectMode::Pointer); let i = apply(bx.cx, attrs); let sret = llvm::CreateStructRetAttr( bx.cx.llcx, @@ -646,7 +685,12 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { for arg in self.args.iter() { match &arg.mode { PassMode::Ignore => {} - PassMode::Indirect { attrs, meta_attrs: None, on_stack: true } => { + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::OnStack, + } => { let i = apply(bx.cx, attrs); let byval = llvm::CreateByValAttr( bx.cx.llcx, @@ -658,11 +702,38 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { &[byval], ); } + PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::AmdgpuKernelArg, + } => { + let i = apply(bx.cx, attrs); + let byref = llvm::CreateByRefAttr( + bx.cx.llcx, + bx.cx.type_array(bx.cx.type_i8(), arg.layout.size.bytes()), + ); + attributes::apply_to_callsite( + callsite, + llvm::AttributePlace::Argument(i), + &[byref], + ); + } PassMode::Direct(attrs) - | PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => { + | PassMode::Indirect { + attrs, + meta_attrs: None, + address_space: _, + mode: IndirectMode::Pointer, + } => { apply(bx.cx, attrs); } - PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack: _ } => { + PassMode::Indirect { + attrs, + meta_attrs: Some(meta_attrs), + address_space: _, + mode: _, + } => { apply(bx.cx, attrs); apply(bx.cx, meta_attrs); } 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_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d1cdf7bada0b1..63fcdf8dcbd9c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2015,6 +2015,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustCreateDereferenceableAttr(C: &Context, bytes: u64) -> &Attribute; pub(crate) fn LLVMRustCreateDereferenceableOrNullAttr(C: &Context, bytes: u64) -> &Attribute; pub(crate) fn LLVMRustCreateByValAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; + pub(crate) fn LLVMRustCreateByRefAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; pub(crate) fn LLVMRustCreateStructRetAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; pub(crate) fn LLVMRustCreateElementTypeAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; pub(crate) fn LLVMRustCreateUWTableAttr(C: &Context, async_: bool) -> &Attribute; diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 5452f4abc5c33..89e4d60656d34 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -122,6 +122,10 @@ pub(crate) fn CreateByValAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll At unsafe { LLVMRustCreateByValAttr(llcx, ty) } } +pub(crate) fn CreateByRefAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll Attribute { + unsafe { LLVMRustCreateByRefAttr(llcx, ty) } +} + pub(crate) fn CreateStructRetAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll Attribute { unsafe { LLVMRustCreateStructRetAttr(llcx, ty) } } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 6b0def4ffa182..f99009a0f4243 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt}; use rustc_session::config::OptLevel; use rustc_span::{Span, Spanned, bug, span_bug}; -use rustc_target::callconv::{ArgAbi, ArgAttributes, CastTarget, FnAbi, PassMode}; +use rustc_target::callconv::{ArgAbi, ArgAttributes, CastTarget, FnAbi, IndirectMode, PassMode}; use tracing::{debug, info}; use super::operand::OperandRef; @@ -1257,7 +1257,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (args, None) }; - // Special logic for tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments. + // Special logic for tail calls with `PassMode::Indirect { mode: IndirectMode::Pointer, .. }` arguments. // // Normally an indirect argument that is allocated in the caller's stack frame // would be passed as a pointer into the callee's stack frame. @@ -1282,10 +1282,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let mut tail_call_temporaries = vec![]; if kind == CallKind::Tail { tail_call_temporaries = vec![None; first_args.len()]; - // Copy the arguments that use `PassMode::Indirect { on_stack: false , ..}` + // Copy the arguments that use `PassMode::Indirect { mode: IndirectMode::Pointer , ..}` // to temporary stack allocations. See the comment above. for (i, arg) in first_args.iter().enumerate() { - if !matches!(fn_abi.args[i].mode, PassMode::Indirect { on_stack: false, .. }) { + if !matches!( + fn_abi.args[i].mode, + PassMode::Indirect { mode: IndirectMode::Pointer, .. } + ) { continue; } @@ -1353,10 +1356,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - let by_move = if let PassMode::Indirect { on_stack: false, .. } = fn_abi.args[i].mode + let by_move = if let PassMode::Indirect { mode: IndirectMode::Pointer, .. } = + fn_abi.args[i].mode && kind == CallKind::Tail { - // Special logic for tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments. + // Special logic for tail calls with `PassMode::Indirect { mode: IndirectMode::Pointer, .. }` arguments. // // Normally an indirect argument that is allocated in the caller's stack frame // would be passed as a pointer into the callee's stack frame. @@ -1977,14 +1981,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => bug!("codegen_argument: {:?} invalid for pair argument", op), }, - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => match op.val { - Ref(PlaceValue { llval: a, llextra: Some(b), .. }) => { - llargs.push(a); - llargs.push(b); - return; + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { + match op.val { + Ref(PlaceValue { llval: a, llextra: Some(b), .. }) => { + llargs.push(a); + llargs.push(b); + return; + } + _ => bug!("codegen_argument: {:?} invalid for unsized indirect argument", op), } - _ => bug!("codegen_argument: {:?} invalid for unsized indirect argument", op), - }, + } _ => {} } @@ -2014,7 +2020,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { PassMode::Ignore | PassMode::Pair(..) => unreachable!("handled above"), }, Ref(op_place_val) => match arg.mode { - PassMode::Indirect { attrs, on_stack, .. } => { + PassMode::Indirect { attrs, mode, .. } => { + if mode == IndirectMode::AmdgpuKernelArg { + bug!("{op:?} passed as amdgpu kernel argument with abi {arg:?}"); + } // For `foo(packed.large_field)`, and types with <4 byte alignment on x86, // alignment requirements may be higher than the type's alignment, so copy // to a higher-aligned alloca. @@ -2023,7 +2032,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None => arg.layout.align.abi, }; // Copy to an alloca when the argument is neither by-val nor by-move. - if op_place_val.align < required_align || (!on_stack && !by_move) { + if op_place_val.align < required_align + || (mode == IndirectMode::Pointer && !by_move) + { let scratch = PlaceValue::alloca(bx, arg.layout.size, required_align); bx.lifetime_start(scratch.llval, arg.layout.size); op.store_with_annotation(bx, scratch.with_type(arg.layout)); @@ -2036,8 +2047,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => (op_place_val.llval, op_place_val.align, true), }, ZeroSized => match arg.mode { - PassMode::Indirect { on_stack, .. } => { - if on_stack { + PassMode::Indirect { mode, .. } => { + if mode == IndirectMode::AmdgpuKernelArg { + bug!("{op:?} passed as amdgpu kernel argument with abi {arg:?}"); + } + if mode == IndirectMode::OnStack { // It doesn't seem like any target can have `byval` ZSTs, so this assert // is here to replace a would-be untested codepath. bug!("ZST {op:?} passed on stack with abi {arg:?}"); diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index b5cecf4b5c434..aefa8356536dc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -8,7 +8,7 @@ use rustc_middle::mir::{Body, Local, UnwindTerminateReason, traversal}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, bug, span_bug}; -use rustc_target::callconv::{FnAbi, PassMode}; +use rustc_target::callconv::{FnAbi, IndirectMode, PassMode}; use tracing::{debug, instrument}; use crate::base; @@ -561,15 +561,21 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( match arg.mode { // Sized indirect arguments - PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { + PassMode::Indirect { attrs, meta_attrs: None, address_space: _, mode } => { // Don't copy an indirect argument to an alloca, the caller already put it // in a temporary alloca and gave it up. + // AmdgpuKernelArg/byref arguments must not be modified, so always create a + // local alloca for them. + // If the argument is underaligned, then we need to copy it to a higher-aligned + // alloca. // FIXME: lifetimes + let mut needs_alloca = mode == IndirectMode::AmdgpuKernelArg; if let Some(pointee_align) = attrs.pointee_align && pointee_align < arg.layout.align.abi { - // ...unless the argument is underaligned, then we need to copy it to - // a higher-aligned alloca. + needs_alloca = true; + } + if needs_alloca { let tmp = PlaceRef::alloca(bx, arg.layout); bx.store_fn_arg(arg, &mut llarg_idx, tmp); LocalRef::Place(tmp) @@ -580,7 +586,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } // Unsized indirect arguments - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } => { // As the storage for the indirect argument lives during // the whole function call, we just copy the wide pointer. let llarg = bx.get_param(llarg_idx); 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_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 7c4e9fb649217..bedcfe998b638 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -87,21 +87,22 @@ pub struct SpanLabel { pub struct MultiSpan { primary_spans: Vec, span_labels: Vec<(Span, DiagMessage)>, + span_context: Vec, } impl MultiSpan { #[inline] pub fn new() -> MultiSpan { - MultiSpan { primary_spans: vec![], span_labels: vec![] } + MultiSpan { primary_spans: vec![], span_labels: vec![], span_context: vec![] } } pub fn from_span(primary_span: Span) -> MultiSpan { - MultiSpan { primary_spans: vec![primary_span], span_labels: vec![] } + MultiSpan { primary_spans: vec![primary_span], span_labels: vec![], span_context: vec![] } } pub fn from_spans(mut vec: Vec) -> MultiSpan { vec.sort(); - MultiSpan { primary_spans: vec, span_labels: vec![] } + MultiSpan { primary_spans: vec, span_labels: vec![], span_context: vec![] } } pub fn push_primary_span(&mut self, primary_span: Span) { @@ -112,6 +113,10 @@ impl MultiSpan { self.span_labels.push((span, label.into())); } + pub fn push_span_context(&mut self, span: Span) { + self.span_context.push(span); + } + pub fn push_span_diag(&mut self, span: Span, diag: DiagMessage) { self.span_labels.push((span, diag)); } @@ -182,6 +187,10 @@ impl MultiSpan { span_labels } + pub fn span_context(&self) -> &[Span] { + &self.span_context + } + /// Returns the span labels as contained by `MultiSpan`. pub fn span_labels_raw(&self) -> &[(Span, DiagMessage)] { &self.span_labels diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index f075ff21bc7bc..d7e38148806f7 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -715,6 +715,18 @@ fn collect_annotations( } } + for span in msp.span_context() { + let file = sm.lookup_source_file(span.lo()); + let ann = Annotation { kind: AnnotationKind::Visible, span: *span, label: None }; + if let Some((_, annotations)) = + output.iter_mut().find(|(f, _)| f.stable_id == file.stable_id) + { + annotations.push(ann); + } else { + output.push((file, vec![ann])); + } + } + // Sort annotations within each file by line number for (_, ann) in output.iter_mut() { ann.sort_by_key(|a| { diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 5017f1228f2d0..987c9818bf0e5 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -617,6 +617,12 @@ impl<'a, G> Diag<'a, G> { self } } + with_fn! { with_span_context, + pub fn span_context(&mut self, span: Span) -> &mut Self { + self.span.push_span_context(span); + self + } } + with_fn! { with_span_labels, /// Labels all the given spans with the provided label. /// See [`Self::span_label()`] for more information. 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/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 40aeb3b8d2af7..4061d247f59f1 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -2123,11 +2123,11 @@ fn compare_generic_param_kinds<'tcx>( }; let trait_header_span = tcx.def_ident_span(tcx.parent(trait_item.def_id)).unwrap(); - err.span_label(trait_header_span, ""); + err.span_context(trait_header_span); err.span_label(param_trait_span, make_param_message("expected", param_trait)); let impl_header_span = tcx.def_span(tcx.parent(impl_item.def_id)); - err.span_label(impl_header_span, ""); + err.span_context(impl_header_span); err.span_label(param_impl_span, make_param_message("found", param_impl)); let reported = err.emit_unless_delay(delay); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 9aded3aeb9318..429f45c2d8358 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>>, @@ -927,7 +926,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 +937,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 +1257,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 +1298,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/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 8b0da8c28d1e1..7f0c6910cc425 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -476,12 +476,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); debug!(?alias_args); - ty::AliasTerm::new_from_def_id( - tcx, - assoc_item.def_id, - alias_args, - ty::AliasConstInherentArgsKind::WithSelf, - ) + let kind = if let ty::AssocTag::Const = assoc_tag { + ty::AliasTermKind::ProjectionConst { def_id: assoc_item.def_id } + } else { + ty::AliasTermKind::ProjectionTy { def_id: assoc_item.def_id } + }; + ty::AliasTerm::new_from_args(tcx, kind, alias_args) }) }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 59ce0cded29e4..2a3728573f31e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -480,12 +480,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &item_segment, trait_ref.args, ); - ty::AliasTerm::new_from_def_id( - tcx, - assoc_item.def_id, - alias_args, - ty::AliasConstInherentArgsKind::WithSelf, - ) + let kind = ty::AliasTermKind::ProjectionConst { + def_id: assoc_item.def_id, + }; + ty::AliasTerm::new_from_args(tcx, kind, alias_args) }); // FIXME(mgca): code duplication with other places we lower 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..e89caa6aeff8c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1608,12 +1608,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } - Ok(TypeRelativePath::AssocItem(ty::AliasTerm::new_from_def_id( - tcx, - item_def_id, - args, - ty::AliasConstInherentArgsKind::WithSelf, - ))) + let kind = match mode { + LowerTypeRelativePathMode::Type(..) => { + ty::AliasTermKind::ProjectionTy { def_id: item_def_id } + } + LowerTypeRelativePathMode::Const => { + ty::AliasTermKind::ProjectionConst { def_id: item_def_id } + } + }; + + Ok(TypeRelativePath::AssocItem(ty::AliasTerm::new_from_args(tcx, kind, args))) } /// Resolve a [type-relative](hir::QPath::TypeRelative) (and type-level) path. @@ -1947,11 +1951,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.check_const_item_in_type_system(item_def_id, span)?; let alias_const = ty::AliasConst::new( tcx, - ty::AliasConstKind::new_from_def_id( - tcx, - item_def_id, - ty::AliasConstInherentArgsKind::WithSelf, - ), + ty::AliasConstKind::Projection { def_id: item_def_id }, item_args, ); Ok(Const::new_alias(tcx, ty::IsRigid::No, alias_const)) @@ -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( @@ -2911,15 +2891,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Const::new_alias( tcx, ty::IsRigid::No, - ty::AliasConst::new( - tcx, - ty::AliasConstKind::new_from_def_id( - tcx, - did, - ty::AliasConstInherentArgsKind::WithSelf, - ), - args, - ), + ty::AliasConst::new(tcx, ty::AliasConstKind::Free { def_id: did }, args), ) } Res::Def(kind @ DefKind::Ctor(ctor_of, CtorKind::Const), did) => { 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_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 1ca644d759d5a..07c98f884cebb 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -3908,7 +3908,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let tcx = self.tcx; if tcx.sess.source_map().is_multiline(sugg_span) { - err.span_label(sugg_span.with_hi(span.lo()), ""); + err.span_context(sugg_span.with_hi(span.lo())); } if let Some(within_macro_span) = within_macro_span { err.span_label(within_macro_span, "due to this macro variable"); diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 81cdd278c3bc7..caffef6a217a8 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1914,9 +1914,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { last_subpat_span, format!("expected {} field{}, found {}", fields.len(), fields_ending, subpats.len()), ); - if self.tcx.sess.source_map().is_multiline(qpath.span().between(last_subpat_span)) { - err.span_label(qpath.span(), ""); - } + err.span_context(qpath.span()); if self.tcx.sess.source_map().is_multiline(def_ident_span.between(last_field_def_span)) { err.span_label(def_ident_span, format!("{} defined here", res.descr())); } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 161b5bdb952d3..bc8fa60b66a52 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -480,6 +480,11 @@ extern "C" LLVMAttributeRef LLVMRustCreateByValAttr(LLVMContextRef C, return wrap(Attribute::getWithByValType(*unwrap(C), unwrap(Ty))); } +extern "C" LLVMAttributeRef LLVMRustCreateByRefAttr(LLVMContextRef C, + LLVMTypeRef Ty) { + return wrap(Attribute::getWithByRefType(*unwrap(C), unwrap(Ty))); +} + extern "C" LLVMAttributeRef LLVMRustCreateStructRetAttr(LLVMContextRef C, LLVMTypeRef Ty) { return wrap(Attribute::getWithStructRetType(*unwrap(C), unwrap(Ty))); diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 61af59cc47aa0..a73e94dd076e7 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1,7 +1,6 @@ // Decoding metadata from a single crate's metadata use std::iter::TrustedLen; -use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; use std::sync::{Arc, OnceLock}; use std::{io, mem}; @@ -23,10 +22,9 @@ use rustc_hir::def::Res; use rustc_hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_index::Idx; -use rustc_middle::implement_ty_decoder; use rustc_middle::middle::lib_features::LibFeatures; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; -use rustc_middle::ty::codec::TyDecoder; +use rustc_middle::ty::codec::{TyDecoder, forward_all_decoder_methods_to}; use rustc_middle::ty::{RestrictionKind, Visibility}; use rustc_proc_macro::bridge::client::Client as ProcMacroClient; use rustc_serialize::opaque::MemDecoder; @@ -236,25 +234,11 @@ pub(super) struct MetadataDecodeContext<'a, 'tcx> { impl<'a, 'tcx> LazyDecoder for MetadataDecodeContext<'a, 'tcx> { fn set_lazy_state(&mut self, state: LazyState) { - self.lazy_state = state; + self.blob_decoder.lazy_state = state; } fn get_lazy_state(&self) -> LazyState { - self.lazy_state - } -} - -impl<'a, 'tcx> DerefMut for MetadataDecodeContext<'a, 'tcx> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.blob_decoder - } -} - -impl<'a, 'tcx> Deref for MetadataDecodeContext<'a, 'tcx> { - type Target = BlobDecodeContext<'a>; - - fn deref(&self) -> &Self::Target { - &self.blob_decoder + self.blob_decoder.lazy_state } } @@ -689,13 +673,12 @@ impl Decodable for LazyTable { } } -mod meta { - use super::*; - implement_ty_decoder!(MetadataDecodeContext<'a, 'tcx>); +impl<'a, 'tcx> Decoder for MetadataDecodeContext<'a, 'tcx> { + forward_all_decoder_methods_to!(|self| self.blob_decoder.opaque); } -mod blob { - use super::*; - implement_ty_decoder!(BlobDecodeContext<'a>); + +impl<'a> Decoder for BlobDecodeContext<'a> { + forward_all_decoder_methods_to!(|self| self.opaque); } impl MetadataBlob { diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 9b12bd144a111..83f62ee65025c 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -1,6 +1,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use rustc_abi::{HasDataLayout, Size}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_macros::{Lift, StableHash, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::{DUMMY_SP, RemapPathScopeComponents, Span, Symbol, bug}; @@ -478,15 +479,21 @@ impl<'tcx> UnevaluatedConst<'tcx> { #[inline] pub fn shrink(self, tcx: TyCtxt<'tcx>) -> ty::AliasConst<'tcx> { assert_eq!(self.promoted, None); - ty::AliasConst::new( - tcx, - ty::AliasConstKind::new_from_def_id( - tcx, - self.def, - ty::AliasConstInherentArgsKind::Impl, - ), - self.args, - ) + + let kind = match tcx.def_kind(self.def) { + DefKind::AssocConst => { + if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(self.def)) { + ty::AliasConstKind::InherentImpl { def_id: self.def } + } else { + ty::AliasConstKind::Projection { def_id: self.def } + } + } + DefKind::Const => ty::AliasConstKind::Free { def_id: self.def }, + DefKind::AnonConst => ty::AliasConstKind::Anon { def_id: self.def }, + kind => bug!("unexpected DefKind in MIR UnevaluatedConst: {kind:?}"), + }; + + ty::AliasConst::new(tcx, kind, self.args) } } 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_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 2ddcfc7ee01a5..20c1c5c16c95c 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -28,7 +28,7 @@ use crate::dep_graph::{DepNodeIndex, QuerySideEffect, SerializedDepNodeIndex}; use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use crate::mir::{self, interpret}; use crate::mono::MonoItem; -use crate::ty::codec::{RefDecodable, TyDecoder, TyEncoder}; +use crate::ty::codec::{RefDecodable, TyDecoder, TyEncoder, forward_all_decoder_methods_to}; use crate::ty::{self, Ty, TyCtxt}; const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE; @@ -529,7 +529,9 @@ impl<'a, 'tcx> rustc_type_ir::InternerDecoder for CacheDecoder<'a, 'tcx> { } } -crate::implement_ty_decoder!(CacheDecoder<'a, 'tcx>); +impl<'a, 'tcx> Decoder for CacheDecoder<'a, 'tcx> { + forward_all_decoder_methods_to!(|self| self.opaque); +} // This ensures that the `Decodable::decode` specialization for `Vec` is used // when a `CacheDecoder` is passed to `Decodable::decode`. Unfortunately, we have to manually opt diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 1a63c05e06ced..9d4899d6fd450 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -323,57 +323,45 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for AdtDef<'tcx> { } } -#[macro_export] -macro_rules! __impl_decoder_methods { - ($($name:ident -> $ty:ty;)*) => { - $( - #[inline] - fn $name(&mut self) -> $ty { - self.opaque.$name() - } - )* - } -} - -#[macro_export] -macro_rules! implement_ty_decoder { - ($DecoderName:ident <$($typaram:tt),*>) => { - mod __ty_decoder_impl { - use rustc_serialize::Decoder; - - use super::$DecoderName; - - impl<$($typaram ),*> Decoder for $DecoderName<$($typaram),*> { - $crate::__impl_decoder_methods! { - read_usize -> usize; - read_u128 -> u128; - read_u64 -> u64; - read_u32 -> u32; - read_u16 -> u16; - read_u8 -> u8; - - read_isize -> isize; - read_i128 -> i128; - read_i64 -> i64; - read_i32 -> i32; - read_i16 -> i16; - } - - #[inline] - fn read_raw_bytes(&mut self, len: usize) -> &[u8] { - self.opaque.read_raw_bytes(len) - } - - #[inline] - fn peek_byte(&self) -> u8 { - self.opaque.peek_byte() - } - - #[inline] - fn position(&self) -> usize { - self.opaque.position() - } - } +/// Declares implementations of all [`Decoder`](rustc_serialize::Decoder) methods, +/// each of which forwards to a method of the same name on some underlying decoder, +/// typically a field of type [`MemDecoder`](rustc_serialize::opaque::MemDecoder). +/// +/// Call this macro within an impl block `impl Decoder for $MyDecoder { ... }`. +pub macro forward_all_decoder_methods_to { + ( + // Make the caller provide an explicit `self` (using closure syntax), + // so that `$inner:expr` can refer to `self` without violating hygiene. + // + // This isn't an actual closure, because it needs to work for both + // `&self` and `&mut self` methods. + |$self:ident| $inner:expr + ) => { + #[inline] fn read_usize(&mut $self) -> usize { $inner.read_usize() } + #[inline] fn read_u128 (&mut $self) -> u128 { $inner.read_u128() } + #[inline] fn read_u64 (&mut $self) -> u64 { $inner.read_u64() } + #[inline] fn read_u32 (&mut $self) -> u32 { $inner.read_u32() } + #[inline] fn read_u16 (&mut $self) -> u16 { $inner.read_u16() } + #[inline] fn read_u8 (&mut $self) -> u8 { $inner.read_u8() } + #[inline] fn read_isize(&mut $self) -> isize { $inner.read_isize() } + #[inline] fn read_i128 (&mut $self) -> i128 { $inner.read_i128() } + #[inline] fn read_i64 (&mut $self) -> i64 { $inner.read_i64() } + #[inline] fn read_i32 (&mut $self) -> i32 { $inner.read_i32() } + #[inline] fn read_i16 (&mut $self) -> i16 { $inner.read_i16() } + + #[inline] + fn read_raw_bytes(&mut $self, len: usize) -> &[u8] { + $inner.read_raw_bytes(len) + } + + #[inline] + fn peek_byte(&$self) -> u8 { + $inner.peek_byte() + } + + #[inline] + fn position(&$self) -> usize { + $inner.position() } } } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 0cb926ec1c108..50e823f88178f 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -220,71 +220,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.adt_def(adt_def_id) } - fn alias_const_kind_from_def_id( - self, - def_id: Self::DefId, - inherent_args: ty::AliasConstInherentArgsKind, - ) -> ty::AliasConstKind<'tcx> { - match self.def_kind(def_id) { - DefKind::AssocConst => { - if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - match inherent_args { - ty::AliasConstInherentArgsKind::WithSelf => { - ty::AliasConstKind::InherentSelf { def_id } - } - ty::AliasConstInherentArgsKind::Impl => { - ty::AliasConstKind::InherentImpl { def_id } - } - } - } else { - ty::AliasConstKind::Projection { def_id } - } - } - DefKind::Const => ty::AliasConstKind::Free { def_id }, - DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => { - ty::AliasConstKind::Anon { def_id } - } - kind => bug!("unexpected DefKind in AliasConst: {kind:?}"), - } - } - - fn alias_term_kind_from_def_id( - self, - def_id: DefId, - inherent_args: ty::AliasConstInherentArgsKind, - ) -> ty::AliasTermKind<'tcx> { - match self.def_kind(def_id) { - DefKind::AssocTy => { - if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - ty::AliasTermKind::InherentTy { def_id } - } else { - ty::AliasTermKind::ProjectionTy { def_id } - } - } - DefKind::AssocConst => { - if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - match inherent_args { - ty::AliasConstInherentArgsKind::WithSelf => { - ty::AliasTermKind::InherentConstSelf { def_id } - } - ty::AliasConstInherentArgsKind::Impl => { - ty::AliasTermKind::InherentConstImpl { def_id } - } - } - } else { - ty::AliasTermKind::ProjectionConst { def_id } - } - } - DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy { def_id }, - DefKind::TyAlias => ty::AliasTermKind::FreeTy { def_id }, - DefKind::Const => ty::AliasTermKind::FreeConst { def_id }, - DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => { - ty::AliasTermKind::AnonConst { def_id } - } - kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), - } - } - fn trait_ref_and_own_args_for_alias( self, def_id: DefId, diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 6d48831a3d23b..eb9f56d887684 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -73,33 +73,37 @@ pub(crate) fn as_constant_inner<'tcx>( } ExprKind::NamedConst { def_id, args, ref user_ty } => { let user_ty = user_ty.as_ref().and_then(push_cuta); + // Under generic_const_args, `def_id` might be a regular const declared in a trait, but // is `impl`d as a directly represented const. We do not know whether it is here, so we // must use type system normalization for all consts under generic_const_args. // FIXME(generic_const_args): there's a lot to consider here! `Const::Ty` uses valtrees // and `Const::Unevaluated` does not, we should revisit this before stabilization. + let def_kind = tcx.def_kind(def_id); if tcx.features().generic_const_args() - || matches!(tcx.def_kind(def_id), DefKind::Const | DefKind::AssocConst) + || matches!(def_kind, DefKind::Const | DefKind::AssocConst) && tcx.is_direct_const(def_id) { - let uneval = ty::AliasConst::new( - tcx, - ty::AliasConstKind::new_from_def_id( - tcx, - def_id, - ty::AliasConstInherentArgsKind::Impl, - ), - args, - ); - let ct = ty::Const::new_alias(tcx, ty::IsRigid::No, uneval); - + let kind = match def_kind { + DefKind::AssocConst => { + if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(def_id)) + { + ty::AliasConstKind::InherentImpl { def_id } + } else { + ty::AliasConstKind::Projection { def_id } + } + } + DefKind::Const => ty::AliasConstKind::Free { def_id }, + _ => unreachable!(), + }; + let alias = ty::AliasConst::new(tcx, kind, args); + let ct = ty::Const::new_alias(tcx, ty::IsRigid::No, alias); let const_ = Const::Ty(ty, ct); return ConstOperand { span, user_ty, const_ }; } let uneval = mir::UnevaluatedConst::new(def_id, args); let const_ = Const::Unevaluated(uneval, ty); - ConstOperand { user_ty, span, const_ } } ExprKind::ConstParam { param, def_id: _ } => { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index c2d1739d37187..feef8d24bd9ca 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -327,7 +327,7 @@ impl<'tcx> ThirBuildCx<'tcx> { } else if let hir::ExprKind::Path(ref qpath) = source.kind && let res = self.typeck_results.qpath_res(qpath, source.hir_id) && let ty = self.typeck_results.node_type(source.hir_id) - && let ty::Adt(adt_def, args) = ty.kind() + && let ty::Adt(adt_def, _) = ty.kind() && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res { // Check whether this is casting an enum variant discriminant. @@ -369,6 +369,8 @@ impl<'tcx> ThirBuildCx<'tcx> { // in case we are offsetting from a computed discriminant // and not the beginning of discriminants (which is always `0`) Some(did) => { + let args = self.tcx.mk_args(&[]); + self.tcx.debug_assert_args_compatible(did, args); let kind = ExprKind::NamedConst { def_id: did, args, user_ty: None }; let lhs = self.thir.exprs.push(Expr { temp_scope_id, ty: discr_ty, span, kind }); diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index c8f185b804f6a..befe4d67253a3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1289,7 +1289,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( report_adt_defined_here(cx.tcx, scrut_ty, &witnesses, true) { let mut multi_span = MultiSpan::from_span(adt_def_span); - multi_span.push_span_label(adt_def_span, ""); + multi_span.push_span_context(adt_def_span); for Variant { span } in variants { multi_span.push_span_label(span, "not covered"); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 8aa11ad89f4e9..55eef6006f278 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -84,7 +84,7 @@ impl<'tcx> ConstToPat<'tcx> { && let Some(def_id) = def_id.as_local() { // Include the container item in the output. - err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), ""); + err.span_context(self.tcx.def_span(self.tcx.local_parent(def_id))); } if let ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::InherentSelf { def_id } @@ -128,7 +128,7 @@ impl<'tcx> ConstToPat<'tcx> { { // Display the `fn` name as well in the diagnostic, as the generic isn't // in the same line and it could be confusing otherwise. - err.span_label(ident, ""); + err.span_context(ident); } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index ff3f643303972..5b7a4d626aae3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -635,9 +635,17 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { let ty = self.typeck_results.node_type(id); let res = self.typeck_results.qpath_res(qpath, id); - let (def_id, user_ty) = match res { - Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { - (def_id, self.typeck_results.user_provided_types().get(id)) + let kind = match res { + Res::Def(DefKind::Const, def_id) => ty::AliasConstKind::Free { def_id }, + + Res::Def(DefKind::AssocConst, def_id) => { + if let DefKind::Impl { of_trait: false } = + self.tcx.def_kind(self.tcx.parent(def_id)) + { + ty::AliasConstKind::InherentImpl { def_id } + } else { + ty::AliasConstKind::Projection { def_id } + } } _ => { @@ -649,26 +657,13 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { // Lower the named constant to a THIR pattern. let args = self.typeck_results.node_args(id); - // FIXME(mgca): we will need to special case IACs here to have type system compatible - // generic args, instead of how we represent them in body expressions. - let c = ty::Const::new_alias( - self.tcx, - ty::IsRigid::No, - ty::AliasConst::new( - self.tcx, - ty::AliasConstKind::new_from_def_id( - self.tcx, - def_id, - ty::AliasConstInherentArgsKind::Impl, - ), - args, - ), - ); + let alias = ty::AliasConst::new(self.tcx, kind, args); + let c = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias); let mut pattern = self.const_to_pat(c, ty, id, span); // If this is an associated constant with an explicit user-written // type, add an ascription node (e.g. ` as MyTrait>::CONST`). - if let Some(&user_ty) = user_ty { + if let Some(&user_ty) = self.typeck_results.user_provided_types().get(id) { let annotation = CanonicalUserTypeAnnotation { user_ty: Box::new(user_ty), span, diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 5bba125aefc58..8814670ca4300 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -135,7 +135,7 @@ impl<'tcx> Visitor<'tcx> for DeduceParamAttrs { } // Like a call, but more conservative because the backend may introduce writes to an - // argument if the argument is passed as `PassMode::Indirect { on_stack: false, ... }`. + // argument if the argument is passed as `PassMode::Indirect { mode: IndirectMode::Pointer, ... }`. TerminatorKind::TailCall { .. } => { for usage in self.usage.iter_mut() { *usage |= UsageSummary::MUTATE; diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 894f209f9b473..48a0caa4979bb 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -1,5 +1,6 @@ use rustc_abi::Integer; use rustc_const_eval::const_eval::mk_eval_cx_for_const_val; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::*; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::util::Discr; @@ -44,6 +45,7 @@ struct SimplifyMatch<'tcx, 'a> { discr: &'a Operand<'tcx>, discr_local: Option, discr_ty: Ty<'tcx>, + borrowed_locals: Option>, } impl<'tcx, 'a> SimplifyMatch<'tcx, 'a> { @@ -228,7 +230,7 @@ impl<'tcx, 'a> SimplifyMatch<'tcx, 'a> { /// ``` /// This will simplify into a copy statement. fn unify_by_copy( - &self, + &mut self, dest: Place<'tcx>, rvals: &[(u128, &Rvalue<'tcx>)], ) -> Option> { @@ -258,6 +260,20 @@ impl<'tcx, 'a> SimplifyMatch<'tcx, 'a> { return None; }; + if copy_src_place.is_indirect() { + // If the src place is indirect, only permit generating the copy when the dest place is + // never borrowed. + let borrowed_locals = self + .borrowed_locals + .get_or_insert_with(|| rustc_mir_dataflow::impls::borrowed_locals(self.body)); + if borrowed_locals.contains(dest.local) { + return None; + } + } else if copy_src_place.local == dest.local { + // Also forbid the case where the source and dest are fields of the same local + return None; + } + for &(case, rvalue) in rvals.iter() { match rvalue { // Check if `_3 = const Foo::B` can be transformed to `_3 = copy *_1`. @@ -385,6 +401,7 @@ fn simplify_match<'tcx>( discr, discr_local: None, discr_ty: discr.ty(body.local_decls(), tcx), + borrowed_locals: None, }; let reachable_cases: Vec<_> = targets.iter().filter(|&(_, bb)| !body.basic_blocks[bb].is_empty_unreachable()).collect(); diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index b760ed98c7111..c6d1d2c77a13f 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -41,6 +41,19 @@ pub struct ArgAbi { pub mode: PassMode, } +/// Different modes in which indirect arguments can be passed. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize)] +pub enum IndirectMode { + /// Passed as a normal pointer, nothing special. + Pointer, + /// The value is placed at a fixed stack offset rather than passed as a regular pointer + /// argument. + OnStack, + /// Similar to `OnStack` except that the pointer does not necessarily point to the stack, no + /// extra copy is made, and the passed argument should not be modified. + AmdgpuKernelArg, +} + /// How a function argument should be passed in to the target function. /// /// The pass mode is determined by the platform's calling convention and the @@ -74,14 +87,13 @@ pub enum PassMode { /// Pass the argument indirectly via a pointer. /// /// The caller places the value in memory and passes a pointer to it. - /// When `on_stack` is true, the value is placed at a fixed stack offset - /// rather than passed as a regular pointer argument. Indirect { attrs: ArgAttributes, /// Attributes for the metadata pointer (vtable or length) of unsized arguments. /// Only present for unsized types (e.g., `dyn Trait`, `[T]`). meta_attrs: Option, - on_stack: bool, + address_space: Option, + mode: IndirectMode, }, } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 766c522958db7..65b8e9bd72761 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -11,9 +11,9 @@ use rustc_target::callconv; use crate::IndexedVal; use crate::abi::{ AddressSpace, ArgAbi, ArgAttributes, ArgExtension, CallConvention, CastTarget, FieldsShape, - FloatLength, FnAbi, IntegerLength, IntegerType, Layout, LayoutShape, NumScalableVectors, - PassMode, Primitive, Reg, RegKind, ReprFlags, ReprOptions, Scalar, TagEncoding, TyAndLayout, - Uniform, ValueRepr, VariantFields, VariantsShape, WrappingRange, + FloatLength, FnAbi, IndirectMode, IntegerLength, IntegerType, Layout, LayoutShape, + NumScalableVectors, PassMode, Primitive, Reg, RegKind, ReprFlags, ReprOptions, Scalar, + TagEncoding, TyAndLayout, Uniform, ValueRepr, VariantFields, VariantsShape, WrappingRange, }; use crate::compiler_interface::BridgeTys; use crate::target::MachineSize as Size; @@ -155,6 +155,22 @@ impl<'tcx> Stable<'tcx> for CanonAbi { } } +impl<'tcx> Stable<'tcx> for callconv::IndirectMode { + type T = IndirectMode; + + fn stable<'cx>( + &self, + _tables: &mut Tables<'cx, BridgeTys>, + _cx: &CompilerCtxt<'cx, BridgeTys>, + ) -> Self::T { + match self { + callconv::IndirectMode::Pointer => IndirectMode::Pointer, + callconv::IndirectMode::OnStack => IndirectMode::OnStack, + callconv::IndirectMode::AmdgpuKernelArg => IndirectMode::AmdgpuKernelArg, + } + } +} + impl<'tcx> Stable<'tcx> for callconv::PassMode { type T = PassMode; @@ -172,11 +188,14 @@ impl<'tcx> Stable<'tcx> for callconv::PassMode { callconv::PassMode::Cast { pad_i32_count, cast } => { PassMode::Cast { pad_i32_count: *pad_i32_count, cast: cast.stable(tables, cx) } } - callconv::PassMode::Indirect { attrs, meta_attrs, on_stack } => PassMode::Indirect { - attrs: attrs.stable(tables, cx), - meta_attrs: meta_attrs.map(|a| a.stable(tables, cx)), - on_stack: *on_stack, - }, + callconv::PassMode::Indirect { attrs, meta_attrs, address_space, mode } => { + PassMode::Indirect { + attrs: attrs.stable(tables, cx), + meta_attrs: meta_attrs.map(|a| a.stable(tables, cx)), + address_space: address_space.stable(tables, cx), + mode: mode.stable(tables, cx), + } + } } } } 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 335cc32ca2ef9..87479a4fbedd0 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_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 2b42d1fdf79bb..017e35056f45b 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -245,24 +245,23 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc .filter(|item| !tcx.generics_require_sized_self(item.def_id)) .map(move |assoc_item| { super_poly_trait_ref.map_bound(|super_trait_ref| { - let projection_term = ty::AliasTerm::new_from_def_id( - tcx, - assoc_item.def_id, - super_trait_ref.args, - ty::AliasConstInherentArgsKind::WithSelf, - ); - let term = tcx.normalize_erasing_regions( + let kind = if assoc_item.is_type() { + ty::AliasTermKind::ProjectionTy { def_id: assoc_item.def_id } + } else { + ty::AliasTermKind::ProjectionConst { def_id: assoc_item.def_id } + }; + let projection_term = + ty::AliasTerm::new_from_args(tcx, kind, super_trait_ref.args); + let term = projection_term.to_term(tcx, ty::IsRigid::No); + let normalized_term = tcx.normalize_erasing_regions( ty::TypingEnv::fully_monomorphized(), - Unnormalized::new_wip(projection_term.to_term(tcx, ty::IsRigid::No)), - ); - debug!( - "Projection {:?} -> {term}", - projection_term.to_term(tcx, ty::IsRigid::No) + Unnormalized::new_wip(term), ); + debug!("Projection {term} -> {normalized_term}"); ty::ExistentialPredicate::Projection( ty::ExistentialProjection::erase_self_ty( tcx, - ty::ProjectionClause { projection_term, term }, + ty::ProjectionClause { projection_term, term: normalized_term }, ), ) }) diff --git a/compiler/rustc_target/src/callconv/amdgpu.rs b/compiler/rustc_target/src/callconv/amdgpu.rs index 98ab3ce8eb746..7a9eeaba19c96 100644 --- a/compiler/rustc_target/src/callconv/amdgpu.rs +++ b/compiler/rustc_target/src/callconv/amdgpu.rs @@ -1,25 +1,60 @@ -use rustc_abi::{HasDataLayout, TyAbiInterface}; +use rustc_abi::{ + AddressSpace, BackendRepr, CanonAbi, HasDataLayout, Reg, RegKind, TyAbiInterface, TyAndLayout, +}; -use crate::callconv::{ArgAbi, FnAbi}; +use crate::callconv::{FnAbi, Uniform}; -fn classify_ret<'a, Ty, C>(_cx: &C, ret: &mut ArgAbi<'a, Ty>) -where - Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout, -{ - ret.extend_integer_width_to(32); -} +// For reference, see llvm-project/clang/lib/CodeGen/Targets/AMDGPU.cpp -fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +/// If the given type is a (potentially nested) struct containing a single scalar, return +/// a `Uniform` for the contained, single element. +fn single_element_struct_to_reg<'a, Ty, C>(cx: &C, ty: TyAndLayout<'a, Ty>) -> Option where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { - arg.make_indirect(); - return; + assert!(ty.is_aggregate(), "Only handles aggregate types"); + if ty.layout.fields.count() != 1 { + return None; + } + let field = ty.field(cx, 0); + match field.backend_repr { + BackendRepr::SimdScalableVector { .. } => panic!("scalable vectors are unsupported"), + BackendRepr::Scalar(_) => { + // Check that the size is the same as the size for ty, so no extra padding + let size = field.layout.size.bytes(); + if ty.layout.size.bytes() != size { + return None; + } + + // clang passes the inner type directly, we emulate it with fitting integer types + match size { + 1 => Some(Uniform::new(Reg::i8(), field.layout.size)), + 2 => Some(Uniform::new(Reg::i16(), field.layout.size)), + 4 => Some(Uniform::new(Reg::i32(), field.layout.size)), + 8 => Some(Uniform::new(Reg::i64(), field.layout.size)), + 16 => Some(Uniform::new(Reg::i128(), field.layout.size)), + s => panic!("Unhandled scalar of size {s} in amdgpu gpu-kernel ABI"), + } + } + BackendRepr::SimdVector { element, .. } => { + // Check that the size is the same as the size for ty, so no extra padding + let size = field.layout.size.bytes(); + if ty.layout.size.bytes() != size { + return None; + } + + // clang passes the inner type directly, we emulate it with a vector of the same type. + // The size is rounded up to the size of the complete type (including alignment). + let reg = Reg { + kind: RegKind::Vector { hint_vector_elem: element.primitive() }, + size: field.layout.size, + }; + Some(Uniform::new(reg, field.layout.size)) + } + BackendRepr::Memory { .. } => single_element_struct_to_reg(cx, field), + BackendRepr::ScalarPair { .. } => None, } - arg.extend_integer_width_to(32); } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) @@ -27,14 +62,25 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if !fn_abi.ret.is_ignore() { - classify_ret(cx, &mut fn_abi.ret); - } + // Kernels cannot return values, so do not handle return types + // Try to fill first registers with values and pass by_ref pointers for later indirect arguments for arg in fn_abi.args.iter_mut() { if arg.is_ignore() { continue; } - classify_arg(cx, arg); + if fn_abi.conv == CanonAbi::GpuKernel { + if arg.layout.is_aggregate() { + if let Some(uniform) = single_element_struct_to_reg(cx, arg.layout) { + // Single element structs are passed directly as the inner type + arg.cast_to(uniform); + } else { + // All other aggregates are passed as by_ref pointer in the constant address space + arg.pass_amdgpu_kernel_arg(Some(AddressSpace::GPU_CONSTANT)); + } + } + } else { + // FIXME: C ABI is not yet implemented + } } } diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 9fe22a3a174b6..474f45b54e9b2 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -36,6 +36,25 @@ mod x86_win32; mod x86_win64; mod xtensa; +/// Different modes in which indirect arguments can be passed. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, StableHash)] +pub enum IndirectMode { + /// Passed as a normal pointer, nothing special. + Pointer, + /// The value should be passed at a fixed stack offset in accordance to + /// the ABI rather than passed using a pointer. This corresponds to the `byval` LLVM argument + /// attribute. The `byval` argument will use a byte array with the same size as the Rust type + /// (which ensures that padding is preserved and that we do not rely on LLVM's struct layout), + /// and will use the alignment specified in `attrs.pointee_align` (if `Some`) or the type's + /// alignment (if `None`). This means that the alignment will not always + /// match the Rust type's alignment; see documentation of `pass_by_stack_offset` for more info. + OnStack, + /// `AmdgpuKernelArg` behaves similar to `OnStack` except that the pointer does not necessarily + /// point to the stack, no extra copy is made, and the passed argument should not be modified. + /// This corresponds to the `byref` LLVM argument attribute. + AmdgpuKernelArg, +} + #[derive(Clone, PartialEq, Eq, Hash, Debug, StableHash)] pub enum PassMode { /// Ignore the argument. @@ -63,16 +82,17 @@ pub enum PassMode { /// The `meta_attrs` value, if any, is for the metadata (vtable or length) of an unsized /// argument. (This is the only mode that supports unsized arguments.) /// - /// `on_stack` defines that the value should be passed at a fixed stack offset in accordance to - /// the ABI rather than passed using a pointer. This corresponds to the `byval` LLVM argument - /// attribute. The `byval` argument will use a byte array with the same size as the Rust type - /// (which ensures that padding is preserved and that we do not rely on LLVM's struct layout), - /// and will use the alignment specified in `attrs.pointee_align` (if `Some`) or the type's - /// alignment (if `None`). This means that the alignment will not always - /// match the Rust type's alignment; see documentation of `pass_by_stack_offset` for more info. + /// `address_space` specifies if the pointer is in a special address space or the default one. /// - /// `on_stack` cannot be true for unsized arguments, i.e., when `meta_attrs` is `Some`. - Indirect { attrs: ArgAttributes, meta_attrs: Option, on_stack: bool }, + /// `mode` can be a special way to pass an argument indirectly. + /// `OnStack` and `AmdgpuKernelArg` cannot be used for unsized arguments, i.e., when + /// `meta_attrs` is `Some`. + Indirect { + attrs: ArgAttributes, + meta_attrs: Option, + address_space: Option, + mode: IndirectMode, + }, } impl PassMode { @@ -89,13 +109,23 @@ impl PassMode { PassMode::Cast { cast: c2, pad_i32_count: pad2 }, ) => c1.eq_abi(c2) && pad1 == pad2, ( - PassMode::Indirect { attrs: a1, meta_attrs: None, on_stack: s1 }, - PassMode::Indirect { attrs: a2, meta_attrs: None, on_stack: s2 }, - ) => a1.eq_abi(a2) && s1 == s2, + PassMode::Indirect { attrs: a1, meta_attrs: None, address_space: as1, mode: m1 }, + PassMode::Indirect { attrs: a2, meta_attrs: None, address_space: as2, mode: m2 }, + ) => a1.eq_abi(a2) && as1 == as2 && m1 == m2, ( - PassMode::Indirect { attrs: a1, meta_attrs: Some(e1), on_stack: s1 }, - PassMode::Indirect { attrs: a2, meta_attrs: Some(e2), on_stack: s2 }, - ) => a1.eq_abi(a2) && e1.eq_abi(e2) && s1 == s2, + PassMode::Indirect { + attrs: a1, + meta_attrs: Some(e1), + address_space: as1, + mode: m1, + }, + PassMode::Indirect { + attrs: a2, + meta_attrs: Some(e2), + address_space: as2, + mode: m2, + }, + ) => a1.eq_abi(a2) && as1 == as2 && e1.eq_abi(e2) && m1 == m2, _ => false, } } @@ -424,7 +454,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { let meta_attrs = layout.is_unsized().then_some(ArgAttributes::new()); - PassMode::Indirect { attrs, meta_attrs, on_stack: false } + PassMode::Indirect { attrs, meta_attrs, address_space: None, mode: IndirectMode::Pointer } } /// Pass this argument indirectly, by passing a (thin or wide) pointer to the argument instead. @@ -435,13 +465,31 @@ impl<'a, Ty> ArgAbi<'a, Ty> { PassMode::Direct(_) | PassMode::Pair(_, _) => { self.mode = Self::indirect_pass_mode(&self.layout); } - PassMode::Indirect { attrs: _, meta_attrs: _, on_stack: false } => { + PassMode::Indirect { + attrs: _, + meta_attrs: _, + address_space: _, + mode: IndirectMode::Pointer, + } => { // already indirect } _ => panic!("Tried to make {:?} indirect", self.mode), } } + /// Pass this argument indirectly, by passing a (thin or wide) pointer to the argument instead. + /// This is valid for both sized and unsized arguments. + #[track_caller] + pub fn make_indirect_addrspace(&mut self, addrspace: AddressSpace) { + self.make_indirect(); + match self.mode { + PassMode::Indirect { ref mut address_space, .. } => { + *address_space = Some(addrspace); + } + _ => unreachable!(), + } + } + /// Same as `make_indirect`, but for arguments that are ignored. Only needed for ABIs that pass /// ZSTs indirectly. #[track_caller] @@ -450,7 +498,12 @@ impl<'a, Ty> ArgAbi<'a, Ty> { PassMode::Ignore => { self.mode = Self::indirect_pass_mode(&self.layout); } - PassMode::Indirect { attrs: _, meta_attrs: _, on_stack: false } => { + PassMode::Indirect { + attrs: _, + meta_attrs: _, + address_space: _, + mode: IndirectMode::Pointer, + } => { // already indirect } _ => panic!("Tried to make {:?} indirect (expected `PassMode::Ignore`)", self.mode), @@ -477,8 +530,8 @@ impl<'a, Ty> ArgAbi<'a, Ty> { assert!(!self.layout.is_unsized(), "used byval ABI for unsized layout"); self.make_indirect(); match self.mode { - PassMode::Indirect { ref mut attrs, meta_attrs: _, ref mut on_stack } => { - *on_stack = true; + PassMode::Indirect { ref mut attrs, meta_attrs: _, address_space: _, ref mut mode } => { + *mode = IndirectMode::OnStack; // Some platforms, like 32-bit x86, change the alignment of the type when passing // `byval`. Account for that. @@ -492,6 +545,22 @@ impl<'a, Ty> ArgAbi<'a, Ty> { } } + /// Pass this argument indirectly. + /// This corresponds to the `byref` LLVM argument attribute. + /// + /// `address_space` specifies the address space of the passed pointer. + pub fn pass_amdgpu_kernel_arg(&mut self, addrspace: Option) { + assert!(!self.layout.is_unsized(), "used amdgpu kernel arg ABI for unsized layout"); + self.make_indirect(); + match self.mode { + PassMode::Indirect { attrs: _, meta_attrs: _, ref mut address_space, ref mut mode } => { + *mode = IndirectMode::AmdgpuKernelArg; + *address_space = addrspace; + } + _ => unreachable!(), + } + } + pub fn extend_integer_width_to(&mut self, bits: u64) { // Only integers have signedness if let BackendRepr::Scalar(scalar) = self.layout.backend_repr @@ -545,11 +614,17 @@ impl<'a, Ty> ArgAbi<'a, Ty> { } pub fn is_sized_indirect(&self) -> bool { - matches!(self.mode, PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ }) + matches!( + self.mode, + PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } + ) } pub fn is_unsized_indirect(&self) -> bool { - matches!(self.mode, PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ }) + matches!( + self.mode, + PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } + ) } pub fn is_ignore(&self) -> bool { @@ -834,7 +909,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { // Compute `Aggregate` ABI. let is_indirect_not_on_stack = - matches!(arg.mode, PassMode::Indirect { on_stack: false, .. }); + matches!(arg.mode, PassMode::Indirect { mode: IndirectMode::Pointer, .. }); assert!(is_indirect_not_on_stack); let size = arg.layout.size; @@ -949,7 +1024,7 @@ mod size_asserts { use super::*; // tidy-alphabetical-start - static_assert_size!(ArgAbi<'_, usize>, 56); - static_assert_size!(FnAbi<'_, usize>, 80); + static_assert_size!(ArgAbi<'_, usize>, 64); + static_assert_size!(FnAbi<'_, usize>, 88); // tidy-alphabetical-end } diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index fd608fcf62919..f51e29b34e1d3 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -167,12 +167,13 @@ pub(crate) fn fill_inregs<'a, Ty, C>( for arg in fn_abi.args.iter_mut() { let attrs = match arg.mode { - PassMode::Ignore | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { + PassMode::Ignore + | PassMode::Indirect { attrs: _, meta_attrs: None, address_space: _, mode: _ } => { continue; } PassMode::Direct(ref mut attrs) => attrs, PassMode::Pair(..) - | PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } + | PassMode::Indirect { attrs: _, meta_attrs: Some(_), address_space: _, mode: _ } | PassMode::Cast { .. } => { unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode) } diff --git a/compiler/rustc_target/src/callconv/xtensa.rs b/compiler/rustc_target/src/callconv/xtensa.rs index 4dc9fad650636..49005adeb33c0 100644 --- a/compiler/rustc_target/src/callconv/xtensa.rs +++ b/compiler/rustc_target/src/callconv/xtensa.rs @@ -7,7 +7,7 @@ use rustc_abi::{BackendRepr, HasDataLayout, Size, TyAbiInterface}; -use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; +use crate::callconv::{ArgAbi, FnAbi, IndirectMode, Reg, Uniform}; use crate::spec::HasTargetSpec; const NUM_ARG_GPRS: u64 = 6; @@ -29,8 +29,8 @@ where classify_arg_ty(cx, arg, &mut arg_gprs_left, true); // Ret args cannot be passed via stack, we lower to indirect and let the backend handle the invisible reference match arg.mode { - super::PassMode::Indirect { attrs: _, meta_attrs: _, ref mut on_stack } => { - *on_stack = false; + super::PassMode::Indirect { attrs: _, meta_attrs: _, address_space: _, ref mut mode } => { + *mode = IndirectMode::Pointer; } _ => {} } 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..c3c647df9ee0f 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs @@ -48,22 +48,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); diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 55140d2c5458d..e8f9ded9562d5 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -12,7 +12,9 @@ use rustc_middle::ty::layout::{ use rustc_middle::ty::{self, InstanceKind, ShimKind, Ty, TyCtxt, Unnormalized}; use rustc_span::def_id::DefId; use rustc_span::{DUMMY_SP, bug}; -use rustc_target::callconv::{AbiMap, ArgAbi, ArgAttribute, ArgAttributes, FnAbi, PassMode}; +use rustc_target::callconv::{ + AbiMap, ArgAbi, ArgAttribute, ArgAttributes, FnAbi, IndirectMode, PassMode, +}; use tracing::debug; pub(crate) fn provide(providers: &mut Providers) { @@ -444,15 +446,15 @@ fn fn_abi_sanity_check<'tcx>( // omitted entirely in the calling convention. assert!(arg.is_ignore()); } - if let PassMode::Indirect { on_stack, .. } = arg.mode + if let PassMode::Indirect { mode, .. } = arg.mode && spec_abi != ExternAbi::RustTail { - assert!(!on_stack, "rustic abi {spec_abi:?} shouldn't use on_stack"); + assert!(mode == IndirectMode::Pointer, "rust abi must use plain pointer mode"); } } else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { assert_matches!( arg.mode, - PassMode::Indirect { on_stack: false, .. }, + PassMode::Indirect { mode: IndirectMode::Pointer, .. }, "the {spec_abi} ABI does not implement `#[rustc_pass_indirectly_in_non_rustic_abis]`" ); } @@ -506,9 +508,9 @@ fn fn_abi_sanity_check<'tcx>( // Indirect returns are arguments from an ABI perspective. fn_arg_attrs_sanity_check(attrs, false); } - PassMode::Indirect { meta_attrs: Some(meta_attrs), attrs, on_stack } => { + PassMode::Indirect { meta_attrs: Some(meta_attrs), attrs, address_space: _, mode } => { // With metadata. Must be unsized and not on the stack. - assert!(arg.layout.is_unsized() && !on_stack); + assert!(arg.layout.is_unsized() && *mode == IndirectMode::Pointer); // Also, must not be `extern` type. let tail = tcx.struct_tail_for_codegen(arg.layout.ty, cx.typing_env); if matches!(tail.kind(), ty::Foreign(..)) { diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 6db234fd886ca..0507265ded631 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -7,7 +7,7 @@ use rustc_middle::thir::visit::Visitor; use rustc_middle::ty::abstract_const::CastKind; use rustc_middle::ty::{self, Expr, LitToConstInput, TyCtxt, TypeVisitableExt}; use rustc_middle::{mir, thir}; -use rustc_span::Span; +use rustc_span::{Span, bug}; use tracing::instrument; use crate::diagnostics::{GenericConstantTooComplex, GenericConstantTooComplexSub}; @@ -70,16 +70,21 @@ fn recurse_build<'tcx>( } &ExprKind::ZstLiteral { user_ty: _ } => ty::Const::zero_sized(tcx, node.ty), &ExprKind::NamedConst { def_id, args, user_ty: _ } => { - let uneval = ty::AliasConst::new( - tcx, - ty::AliasConstKind::new_from_def_id( - tcx, - def_id, - ty::AliasConstInherentArgsKind::Impl, - ), - args, - ); - ty::Const::new_alias(tcx, ty::IsRigid::No, uneval) + let kind = match tcx.def_kind(def_id) { + DefKind::AssocConst => { + if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(def_id)) { + ty::AliasConstKind::InherentImpl { def_id } + } else { + ty::AliasConstKind::Projection { def_id } + } + } + DefKind::Const => ty::AliasConstKind::Free { def_id }, + DefKind::AnonConst => ty::AliasConstKind::Anon { def_id }, + kind => bug!("unexpected DefKind in THIR ExprKind::NamedConst: {kind:?}"), + }; + + let alias = ty::AliasConst::new(tcx, kind, args); + ty::Const::new_alias(tcx, ty::IsRigid::No, alias) } ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param), diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 36cef1c13eb29..dd0578610a2a0 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -146,20 +146,7 @@ pub enum AliasConstKind { Anon { def_id: I::AnonConstId }, } -pub enum AliasConstInherentArgsKind { - WithSelf, - Impl, -} - impl AliasConstKind { - pub fn new_from_def_id( - interner: I, - def_id: I::DefId, - inherent_args: AliasConstInherentArgsKind, - ) -> Self { - interner.alias_const_kind_from_def_id(def_id, inherent_args) - } - pub fn is_direct_const(self, interner: I) -> bool { interner.is_direct_const(self) } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 31a027c15fd01..289f5dc2e1b46 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -86,10 +86,10 @@ pub trait Interner: type AnonConstId: SpecificDefId; type TraitAssocTyId: SpecificDefId + Into - + TryFrom; + + TryFrom; type TraitAssocConstId: SpecificDefId + Into - + TryFrom; + + TryFrom; type TraitAssocTermId: SpecificDefId; type OpaqueTyId: SpecificDefId; type LocalOpaqueTyId: Copy @@ -293,19 +293,6 @@ pub trait Interner: type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef; - fn alias_const_kind_from_def_id( - self, - def_id: Self::DefId, - inherent_args: ty::AliasConstInherentArgsKind, - ) -> ty::AliasConstKind; - - // FIXME: remove in favor of explicit construction - fn alias_term_kind_from_def_id( - self, - def_id: Self::DefId, - inherent_args: ty::AliasConstInherentArgsKind, - ) -> ty::AliasTermKind; - fn trait_ref_and_own_args_for_alias( self, def_id: Self::TraitAssocTermId, diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 7ad23d3e5432a..e66ef5fe99b4a 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -495,6 +495,17 @@ impl ExistentialProjection { ExistentialTraitRef::new_from_args(interner, def_id, args) } + pub fn alias_kind(&self) -> ty::AliasTermKind { + match self.term.kind() { + ty::TermKind::Ty(_) => { + ty::AliasTermKind::ProjectionTy { def_id: self.def_id.try_into().unwrap() } + } + ty::TermKind::Const(_) => { + ty::AliasTermKind::ProjectionConst { def_id: self.def_id.try_into().unwrap() } + } + } + } + pub fn with_self_ty(&self, interner: I, self_ty: I::Ty) -> ProjectionClause { // otherwise the escaping regions would be captured by the binders debug_assert!(!self_ty.has_escaping_bound_vars()); @@ -502,10 +513,7 @@ impl ExistentialProjection { ProjectionClause { projection_term: ty::AliasTerm::new( interner, - interner.alias_term_kind_from_def_id( - self.def_id.into(), - ty::AliasConstInherentArgsKind::WithSelf, - ), + self.alias_kind(), [self_ty.into()].iter().chain(self.args.iter()), ), term: self.term, diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index f6491bac642e3..49fcb38968abe 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -281,16 +281,7 @@ impl Relate for ty::ExistentialProjection { b: ty::ExistentialProjection, ) -> RelateResult> { if a.def_id != b.def_id { - Err(TypeError::ProjectionMismatched(ExpectedFound::new( - relation.cx().alias_term_kind_from_def_id( - a.def_id.into(), - ty::AliasConstInherentArgsKind::WithSelf, - ), - relation.cx().alias_term_kind_from_def_id( - b.def_id.into(), - ty::AliasConstInherentArgsKind::WithSelf, - ), - ))) + Err(TypeError::ProjectionMismatched(ExpectedFound::new(a.alias_kind(), b.alias_kind()))) } else { let term = relation.relate_with_variance( ty::Invariant, diff --git a/compiler/rustc_type_ir/src/term_kind.rs b/compiler/rustc_type_ir/src/term_kind.rs index ed23b196fe269..6a4b1f023dde2 100644 --- a/compiler/rustc_type_ir/src/term_kind.rs +++ b/compiler/rustc_type_ir/src/term_kind.rs @@ -169,16 +169,6 @@ impl AliasTerm { Self::new_from_args(interner, kind, args) } - pub fn new_from_def_id( - interner: I, - def_id: I::DefId, - args: I::GenericArgs, - inherent_args: ty::AliasConstInherentArgsKind, - ) -> AliasTerm { - let kind = interner.alias_term_kind_from_def_id(def_id, inherent_args); - Self::new_from_args(interner, kind, args) - } - pub fn expect_ty(self) -> ty::AliasTy { let kind = match self.kind { AliasTermKind::ProjectionTy { def_id } => ty::AliasTyKind::Projection { def_id }, diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 85ff2fe1dd6ee..8c049b118d703 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -964,43 +964,86 @@ pub trait TryAsDynCompatible<'a>: ptr::Pointee /// Returns `Some(&U)` if `T` can be coerced to the dyn trait type `U`. Otherwise, it returns `None`. /// -/// # Run-time failures +///
/// -/// There are multiple ways to get a `None`, and you need to manually analyze which one it is, as the -/// compiler does not provide any help here. +/// This function is implemented on a best-effort basis. It is not always possible to determine +/// whether a generic type implements a trait; thus, this function may produce false negatives, +/// returning `None` even when `T` implements the requested trait. /// -/// * `T` does not implement `Trait` at all, -/// * `T`'s impl for `Trait` is not fully generic, +/// `try_as_dyn` is guaranteed to return `None` if `T` does *not* implement the requested trait, but +/// it is never guaranteed to return `Some`. It is intended to be used for performance +/// optimizations and debugging, and `try_as_dyn` succeeding for a particular type should never be +/// relied upon for correctness (i.e. callers must behave correctly even if `try_as_dyn` spuriously +/// returns `None`). +/// +///
+/// +/// # Examples of false negatives +/// +/// Some examples of situations where `try_as_dyn::` returns `None` in practice even +/// when `T` implements `Trait`: +/// * `T`'s impl for `Trait` is lifetime-dependent /// * `T`'s impl for `Trait` is a builtin impl (e.g. `dyn Debug` implements `Debug`) +/// * `T`'s impl for `Trait` has a trait bound which requires transitively reasoning about +/// lifetime-dependent or builtin impls /// -/// There is some detailed documentation about this feature at -/// -/// But the gist is summarized below: +/// This list is not exhaustive. There is some detailed documentation about these limitations at +/// But the gist is +/// summarized below: /// -/// ## Lifetime-independent impls +/// ## Lifetime-dependent impls /// /// `try_as_dyn` does not have access to lifetime information, thus it cannot differentiate between -/// `'static`, other lifetimes, and can't reason about outlives bounds on impls. Thus we can only accept -/// impls that do not have `'static` lifetimes, or outlives bounds of any kind. You can have simple -/// trait bounds, and the compiler will transitively only use impls of those simple trait bounds that satisfy -/// the same rules as the main trait you're converting to. +/// `'static` and other lifetimes and cannot reason about outlives bounds on impls. Thus it cannot +/// reason about impls that have `'static` lifetimes or outlives bounds of any kind. +/// +/// The following impls are lifetime-dependent and produce false negatives when used with +/// `try_as_dyn`: +/// +/// ```rust +/// # trait Trait<'a, T> {} +/// # struct Type<'b, U>(&'b U); +/// # use std::fmt::{Debug, Display}; +/// // impl mentions a 'static lifetime +/// impl<'a, T: Debug, U: Display> Trait<'a, T> for Type<'static, U> {} +/// ``` +/// +/// ``` +/// # trait Trait<'a, T> {} +/// # struct Type<'b, U>(&'b U); +/// # use std::fmt::{Debug, Display}; +/// // impl contains an outlives bound +/// impl<'a, 'b, T: Debug, U: Display> Trait<'a, T> for Type<'b, U> +/// where 'b: 'a {} +/// ``` /// -/// An example of a legal impl is: +/// Impls that mention a generic parameter more than once are lifetime-dependent and produce false +/// negatives, even if they don't expressly mention any lifetimes: /// /// ```rust +/// # trait Trait {} +/// // impl mentions T more than once, creating an implied lifetime dependence +/// impl Trait for T {} +/// ``` +/// +/// The following impl is lifetime-**independent**, because even though it *mentions* lifetimes, +/// implementation of the trait is not *conditional* over the lifetimes: +/// ```rust /// # trait Trait<'a, T> {} /// # struct Type<'b, U>(&'b U); /// # use std::fmt::{Debug, Display}; /// impl<'a, 'b, T: Debug, U: Display> Trait<'a, T> for Type<'b, U> {} /// ``` /// -/// Impls without generic parameters at all are also legal, as long as they contain no `'static` lifetimes. +/// Impls without generic parameters at all are also lifetime-independent, as long as they contain +/// no `'static` lifetimes. /// /// ## Builtin impls /// -/// Builtin impls (like `impl Debug for dyn Debug`) have various obscure rules and often are not fully generic. -/// To simplify reasoning about what is allowed and what not, all builtin impls are rejected and will neither -/// directly nor indirectly contribute to a `Some` result. +/// Builtin impls (like `impl Debug for dyn Debug`, or automatic implementations of `Send` and +/// `Sync`) have various obscure rules and often are not fully generic. To simplify reasoning about +/// what is allowed and what not, all builtin impls are rejected and will neither directly nor +/// indirectly contribute to a `Some` result. /// /// # Compile-time failures /// Determining whether `T` can be coerced to the dyn trait type `U` requires compiler trait resolution. @@ -1014,30 +1057,96 @@ pub trait TryAsDynCompatible<'a>: ptr::Pointee /// /// # Examples /// +/// Using `try_as_dyn` to use bytewise comparison instead of PartialEq for certain types, similar to +/// the standard library's optimization for slices: +/// /// ```rust /// #![feature(try_as_dyn)] /// /// use core::any::try_as_dyn; /// -/// trait Animal { -/// fn speak(&self) -> &'static str; +/// /// Compares two objects for equality, +/// fn eq(x: &T, y: &T) -> bool { +/// if try_as_dyn::(&x).is_some() { +/// // T implements BytewiseEq, so we cast the slices to u8 and compare their bytes +/// // instead of calling PartialEq on each individual element. +/// unsafe { +/// // SAFETY: x and y are valid for reads of size_of::() bytes +/// // BytewiseEq trait guarantees we can interperet these bytes as u8's +/// // and compare them for equality +/// let x = &*core::ptr::slice_from_raw_parts( +/// (&raw const *x).cast::(), +/// core::mem::size_of_val(x), +/// ); +/// let y = &*core::ptr::slice_from_raw_parts( +/// (&raw const *y).cast::(), +/// core::mem::size_of_val(y), +/// ); +/// +/// x == y +/// } +/// } else { +/// // T does not implement BytewiseEq, or try_as_dyn returned a false negative. +/// // Fallback to PartialEq. +/// // +/// // BytewiseEq guarantees bytewise comparison and PartialEq will produce the same +/// // results, so our code behaves correctly if try_as_dyn produces false negatives. +/// x == y +/// } /// } /// -/// struct Dog; -/// impl Animal for Dog { -/// fn speak(&self) -> &'static str { "woof" } +/// /// Marker trait for types that can be compared for equality +/// /// using a bytewise comparison (i.e. memcmp). +/// /// +/// /// Implementations must ensure the type contains no uninitialized bytes, +/// /// and that a bytewise comparison will produce the same result as PartialEq. +/// unsafe trait BytewiseEq {} +/// +/// unsafe impl BytewiseEq for u8 {} +/// unsafe impl BytewiseEq for u16 {} +/// unsafe impl BytewiseEq for u32 {} +/// +/// // u16 implements BytewiseEq, so eq:: will use bytewise comparison +/// // (unless try_as_dyn returns a false negative) +/// assert!(eq(&5u16, &5u16)); +/// +/// // f32 does not implement BytewiseEq, so eq:: will use element-wise comparison +/// assert!(eq(&5f32, &5f32)); +/// ``` +/// +/// Using `try_as_dyn` for debugging: +/// +/// ```rust +/// #![feature(try_as_dyn)] +/// +/// use core::any::{try_as_dyn, type_name}; +/// use core::fmt::Debug; +/// +/// /// Prints a value of type T, attempting to use its Debug implementation with try_as_dyn. +/// fn debug_println(x: &T) { +/// if let Some(debug) = try_as_dyn::(x) { +/// println!("{:?}", debug); +/// } else { +/// // T does not implement Debug, or try_as_dyn returned a false negative. +/// // Print the name of the type instead. +/// // +/// // We're not relying on this for correctness; it's just for debugging, +/// // so we can tolerate false negatives. +/// println!("<{}>", type_name::()); +/// } /// } /// -/// struct Rock; // does not implement Animal +/// /// This type does not implement Debug. +/// struct NoDebug; /// -/// let dog = Dog; -/// let rock = Rock; +/// // Prints "Hello, world!" unless try_as_dyn returns a false negative. +/// debug_println(&"Hello, world!"); /// -/// let as_animal: Option<&dyn Animal> = try_as_dyn::(&dog); -/// assert_eq!(as_animal.unwrap().speak(), "woof"); +/// // Prints the name of the type, since it does not have a Debug implementation. +/// debug_println(&NoDebug); /// -/// let not_an_animal: Option<&dyn Animal> = try_as_dyn::(&rock); -/// assert!(not_an_animal.is_none()); +/// // The current implementation of try_as_dyn gives a false positive in this case! +/// debug_println(&"Hello, world!" as &dyn Debug); /// ``` #[must_use] #[unstable(feature = "try_as_dyn", issue = "144361")] @@ -1062,7 +1171,7 @@ pub const fn try_as_dyn<'a, T: ?Sized + 'a, U: TryAsDynCompatible<'a> + ?Sized>( } } -/// Returns `Some(&mut U)` if `T` can be coerced to the trait object type `U`. Otherwise, it returns `None`. +/// Returns `Some(&mut U)` if `T` can be coerced to the dyn trait type `U`. Otherwise, it returns `None`. /// /// See documentation of [try_as_dyn] for details about the behaviour and limitations. #[must_use] 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/num/complex.rs b/library/core/src/num/complex.rs index 73b904d81fc53..66126c52fadad 100644 --- a/library/core/src/num/complex.rs +++ b/library/core/src/num/complex.rs @@ -1,4 +1,4 @@ -use crate::ops::{Add, Sub}; +use crate::ops::{Add, Neg, Sub}; /// A complex number. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -16,11 +16,46 @@ pub struct Complex { impl Complex { /// Create a new complex number from a real and imaginary component. #[must_use] - pub fn new(re: T, im: T) -> Complex { + pub const fn new(re: T, im: T) -> Complex { Complex { re, im } } } +#[unstable(feature = "complex_numbers", issue = "154023")] +impl Default for Complex { + fn default() -> Self { + Self { re: Default::default(), im: Default::default() } + } +} + +#[unstable(feature = "complex_numbers", issue = "154023")] +impl Complex +where + T: Neg, +{ + /// The complex conjugate of a complex number. + /// + /// The conjugate of `a + bi` is `a - bi`: the imaginary component is negated. + /// Geometrically, this is a reflection across the real axis. + #[must_use] + pub fn conjugate(self) -> Self { + Complex { re: self.re, im: -self.im } + } +} + +#[unstable(feature = "complex_numbers", issue = "154023")] +impl Neg for Complex { + type Output = Complex; + + /// Negate a complex number. + /// + /// The negation of `a + bi` is `-a - bi`: both components are negated. + /// Geometrically this is a rotation of 180°. + fn neg(self) -> Self::Output { + Complex::new(-self.re, -self.im) + } +} + #[unstable(feature = "complex_numbers", issue = "154023")] impl Add for Complex { type Output = Complex; diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 58bf0e2d73b97..1003edad24484 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -367,6 +367,7 @@ unsafe impl DerefPure for &mut T {} /// ``` #[lang = "receiver"] #[unstable(feature = "arbitrary_self_types", issue = "44874")] +#[rustc_dyn_incompatible_trait] pub trait Receiver: PointeeSized { /// The target type on which the method may be called. #[rustc_diagnostic_item = "receiver_target"] 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/num/complex.rs b/library/coretests/tests/num/complex.rs index 4260c0a9a27a9..c22c5b9575b3d 100644 --- a/library/coretests/tests/num/complex.rs +++ b/library/coretests/tests/num/complex.rs @@ -1,5 +1,16 @@ use core::num::{Complex, Wrapping}; +#[test] +fn complex_default() { + assert_eq!(Complex::::default(), Complex::new(0, 0)); + assert_eq!(Complex::::default(), Complex::new(0.0, 0.0)); + + // The default is the additive unit. + let a = Complex::new(1, 2); + assert_eq!(a + Complex::::default(), a); + assert_eq!(Complex::::default() + a, a); +} + #[test] fn complex_addition() { let a = Complex::new(1, 2); @@ -42,3 +53,23 @@ fn complex_subtraction() { assert_eq!(a - b, Complex::new(a.re - b.re, a.im - b.im)); assert_eq!(a - 8.0, Complex::new(a.re - 8.0, a.im)); } + +#[test] +fn complex_conjugate() { + assert_eq!(Complex::new(1, 2).conjugate(), Complex::new(1, -2)); + assert_eq!(Complex::new(1, -2).conjugate(), Complex::new(1, 2)); + + assert_eq!(Complex::new(1.0, 2.0).conjugate(), Complex::new(1.0, -2.0)); + assert_eq!(Complex::new(1.0, -2.0).conjugate(), Complex::new(1.0, 2.0)); + assert_eq!(Complex::new(1.0, f32::INFINITY).conjugate(), Complex::new(1.0, f32::NEG_INFINITY)); +} + +#[test] +fn complex_negation() { + assert_eq!(-Complex::new(1, 2), Complex::new(-1, -2)); + assert_eq!(-Complex::new(1, -2), Complex::new(-1, 2)); + + assert_eq!(-Complex::new(1.0, 2.0), Complex::new(-1.0, -2.0)); + assert_eq!(-Complex::new(1.0, -2.0), Complex::new(-1.0, 2.0)); + assert_eq!(-Complex::new(1.0, f32::INFINITY), Complex::new(-1.0, f32::NEG_INFINITY),); +} 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/doc/rustc/src/platform-support/armv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md index c08841489b78b..d9ae98632bbb6 100644 --- a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md @@ -95,7 +95,7 @@ to use these flags.

-Never use the `-fpregs` *target-feature* with the `(arm|thumb)v7r-none-eabi` targets +Never use the `-fpregs` *target-feature* with the `(arm|thumb)v7r-none-eabihf` targets as it will cause compilation units to have different ABIs, which is unsound.
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/tail-call-indirect.rs b/tests/assembly-llvm/tail-call-indirect.rs index 2bc1743a9bafd..918283966b405 100644 --- a/tests/assembly-llvm/tail-call-indirect.rs +++ b/tests/assembly-llvm/tail-call-indirect.rs @@ -10,10 +10,10 @@ #![no_core] #![crate_type = "lib"] -// Test tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments. +// Test tail calls with `PassMode::Indirect { mode: IndirectMode::Pointer, .. }` arguments. // -// Normally an indirect argument with `on_stack: false` would be passed as a pointer to the -// caller's stack frame. For tail calls, that would be unsound, because the caller's stack +// Normally an indirect argument with `mode: IndirectMode::Pointer` would be passed as a pointer to +// the caller's stack frame. For tail calls, that would be unsound, because the caller's stack // frame is overwritten by the callee's stack frame. // // The solution is to write the argument into the caller's argument place (stored somewhere further 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/codegen-llvm/amdgpu-abi/struct-abi.rs b/tests/codegen-llvm/amdgpu-abi/struct-abi.rs new file mode 100644 index 0000000000000..bd74a939af4af --- /dev/null +++ b/tests/codegen-llvm/amdgpu-abi/struct-abi.rs @@ -0,0 +1,187 @@ +//@ add-minicore +//@ compile-flags: --crate-type=rlib --target=amdgcn-amd-amdhsa -Ctarget-cpu=gfx900 +//@ needs-llvm-components: amdgpu +#![feature(no_core, abi_gpu_kernel, repr_simd)] +#![no_core] +#![allow(improper_gpu_kernel_arg)] + +extern crate minicore; +use minicore::num::Complex; + +// Tests from llvm-project/clang/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl + +#[repr(simd)] +pub struct I8X2([i8; 2]); + +#[repr(simd)] +pub struct I16X2([i16; 2]); + +#[repr(simd)] +pub struct I16X3([i16; 3]); + +#[repr(simd)] +pub struct I16X4([i16; 4]); + +#[repr(simd)] +pub struct I32X3([i32; 3]); + +#[repr(simd)] +pub struct I32X4([i32; 4]); + +#[repr(C)] +pub struct SingleElementStructArg { + i: T, +} + +#[repr(C)] +pub struct NestedSingleElementStructArg { + i: SingleElementStructArg, +} + +#[repr(C)] +pub struct StructArg { + i1: i32, + f: f32, + i2: i32, +} + +#[repr(C)] +pub struct StructPaddingArg { + i1: i8, + f: i64, +} + +#[repr(C)] +pub struct StructOfArraysArg { + i1: [i32; 2], + f1: f32, + i2: [i32; 4], + f2: [f32; 3], + i3: i32, +} + +#[repr(C)] +pub struct StructOfStructsArg { + i1: i32, + f1: f32, + s1: StructArg, + i2: i32, +} + +#[repr(C)] +pub union U { + b1: i32, + b2: f32, +} + +#[repr(C)] +pub struct SingleArrayElementStructArg { + i: [i32; 4], +} + +#[repr(C)] +pub struct SingleStructElementStructArgInner { + i: i32, + b: i64, +} + +#[repr(C)] +pub struct SingleStructElementStructArg { + s: SingleStructElementStructArgInner, +} + +#[repr(C)] +pub struct DifferentSizeTypePair { + l: i64, + i: i32, +} + +// CHECK: define amdgpu_kernel void @kernel_single_element_struct_arg(i32 %0) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_single_element_struct_arg(_: SingleElementStructArg) {} + +// CHECK: define amdgpu_kernel void @kernel_nested_single_element_struct_arg(i32 %0) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_nested_single_element_struct_arg( + _: NestedSingleElementStructArg, +) { +} + +// CHECK: define amdgpu_kernel void @kernel_struct_arg(ptr addrspace(4) noalias nofree noundef readnone byref([12 x i8]) align 4 captures(none) dereferenceable(12) {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_struct_arg(_: StructArg) {} + +// CHECK: define amdgpu_kernel void @kernel_struct_padding_arg(ptr addrspace(4) noalias nofree noundef readnone byref([16 x i8]) align 8 captures(none) dereferenceable(16) {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_struct_padding_arg(_: StructPaddingArg) {} + +// CHECK: define amdgpu_kernel void @kernel_struct_of_arrays_arg(ptr addrspace(4) noalias nofree noundef readnone byref([44 x i8]) align 4 captures(none) dereferenceable(44) {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_struct_of_arrays_arg(_: StructOfArraysArg) {} + +// CHECK: define amdgpu_kernel void @kernel_struct_of_structs_arg(ptr addrspace(4) noalias nofree noundef readnone byref([24 x i8]) align 4 captures(none) dereferenceable(24) {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_struct_of_structs_arg(_: StructOfStructsArg) {} + +// CHECK: define amdgpu_kernel void @test_kernel_union_arg(ptr addrspace(4) noalias nofree noundef readnone byref([4 x i8]) align 4 captures(none) dereferenceable(4) {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn test_kernel_union_arg(_: U) {} + +// CHECK: define amdgpu_kernel void @kernel_single_array_element_struct_arg(ptr addrspace(4) noalias nofree noundef readnone byref([16 x i8]) align 4 captures(none) dereferenceable(16) {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_single_array_element_struct_arg(_: SingleArrayElementStructArg) {} + +// CHECK: define amdgpu_kernel void @kernel_single_struct_element_struct_arg(ptr addrspace(4) noalias nofree noundef readnone byref([16 x i8]) align 8 captures(none) dereferenceable(16) {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_single_struct_element_struct_arg( + _: SingleStructElementStructArg, +) { +} + +// CHECK: define amdgpu_kernel void @kernel_different_size_type_pair_arg(ptr addrspace(4) noalias nofree noundef readnone byref([16 x i8]) align 8 captures(none) dereferenceable(16) {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_different_size_type_pair_arg(_: DifferentSizeTypePair) {} + +// CHECK: define amdgpu_kernel void @kernel_complex(ptr addrspace(4) noalias nofree noundef readnone byref([8 x i8]) align 4 captures(none) dereferenceable(8) {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_complex(_: Complex) {} + +// CHECK: define amdgpu_kernel void @kernel_slice(ptr addrspace(4) noalias nofree noundef readnone byref([16 x i8]) align 8 captures(none) dereferenceable(16) {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_slice(_: &[u32]) {} + +// CHECK: define amdgpu_kernel void @kernel_i64(i64 noundef {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_i64(_: i64) {} + +// CHECK: define amdgpu_kernel void @kernel_i64_struct(i64 {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_i64_struct(_: SingleElementStructArg) {} + +// CHECK: define amdgpu_kernel void @kernel_i128_struct(i128 {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_i128_struct(_: SingleElementStructArg) {} + +// CHECK: define amdgpu_kernel void @kernel_i8x2_struct(<2 x i8> {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_i8x2_struct(_: SingleElementStructArg) {} + +// CHECK: define amdgpu_kernel void @kernel_i16x2_struct(<2 x i16> {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_i16x2_struct(_: SingleElementStructArg) {} + +// CHECK: define amdgpu_kernel void @kernel_i16x3_struct(<4 x i16> {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_i16x3_struct(_: SingleElementStructArg) {} + +// CHECK: define amdgpu_kernel void @kernel_i16x4_struct(<4 x i16> {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_i16x4_struct(_: SingleElementStructArg) {} + +// CHECK: define amdgpu_kernel void @kernel_i32x3_struct(<4 x i32> {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_i32x3_struct(_: SingleElementStructArg) {} + +// CHECK: define amdgpu_kernel void @kernel_i32x4_struct(<4 x i32> {{%.+}}) +#[no_mangle] +pub extern "gpu-kernel" fn kernel_i32x4_struct(_: SingleElementStructArg) {} diff --git a/tests/mir-opt/match_branch_simplification_aliasing.aliasing_locals.MatchBranchSimplification.diff b/tests/mir-opt/match_branch_simplification_aliasing.aliasing_locals.MatchBranchSimplification.diff new file mode 100644 index 0000000000000..ccaa78736afe7 --- /dev/null +++ b/tests/mir-opt/match_branch_simplification_aliasing.aliasing_locals.MatchBranchSimplification.diff @@ -0,0 +1,36 @@ +- // MIR for `aliasing_locals` before MatchBranchSimplification ++ // MIR for `aliasing_locals` after MatchBranchSimplification + + fn aliasing_locals(_1: Foo) -> Foo { + let mut _0: Foo; + let mut _2: Foo; + let mut _3: *const Foo; + let mut _4: u8; + + bb0: { + _2 = copy _1; + _3 = &raw const _2; + _4 = discriminant((*_3)); + switchInt(copy _4) -> [0: bb2, 1: bb3, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _2 = Foo::A; + goto -> bb4; + } + + bb3: { + _2 = Foo::B; + goto -> bb4; + } + + bb4: { + _0 = copy _2; + return; + } + } + diff --git a/tests/mir-opt/match_branch_simplification_aliasing.rs b/tests/mir-opt/match_branch_simplification_aliasing.rs new file mode 100644 index 0000000000000..1c81d2efa74b0 --- /dev/null +++ b/tests/mir-opt/match_branch_simplification_aliasing.rs @@ -0,0 +1,116 @@ +//@ test-mir-pass: MatchBranchSimplification + +#![feature(custom_mir, core_intrinsics)] +#![allow(internal_features)] + +use std::intrinsics::mir::*; + +#[repr(u8)] +#[derive(Clone, Copy, PartialEq, Eq)] +enum Foo { + A, + B, + // This variant is not used, but makes the enum BackendRepr::Memory. Without it, the enum is a + // scalar and overlapping copies of it are permitted. + C(u32), +} + +// EMIT_MIR match_branch_simplification_aliasing.aliasing_locals.MatchBranchSimplification.diff +#[inline(never)] +#[custom_mir(dialect = "runtime")] +fn aliasing_locals(init: Foo) -> Foo { + // CHECK-LABEL: fn aliasing_locals(_1 + // CHECK: _2 = copy _1; + // CHECK: _3 = &raw const _2; + // CHECK: _4 = discriminant((*_3)); + // CHECK-NOT: copy (*_3); + // CHECK: switchInt + // CHECK: _2 = Foo::A; + // CHECK: _2 = Foo::B; + // CHECK: _0 = copy _2; + mir! { + let x: Foo; + let p: *const Foo; + let d: u8; + { + x = init; + p = core::ptr::addr_of!(x); + d = Discriminant(*p); + match d { + 0 => bb_a, + 1 => bb_b, + _ => bb_unreachable, + } + } + bb_unreachable = { + Unreachable() + } + bb_a = { + x = Foo::A; + Goto(bb_join) + } + bb_b = { + x = Foo::B; + Goto(bb_join) + } + bb_join = { + RET = x; + Return() + } + } +} + +union U { + a: Foo, + b: Foo, +} + +// EMIT_MIR match_branch_simplification_aliasing.union_fields.MatchBranchSimplification.diff +#[inline(never)] +#[custom_mir(dialect = "runtime")] +fn union_fields(init: Foo) -> Foo { + // CHECK-LABEL: fn union_fields(_1 + // CHECK: (_2.1: Foo) = copy _1; + // CHECK: _3 = discriminant((_2.1: Foo)); + // CHECK-NOT: copy(_2.1: Foo); + // CHECK: switchInt + // CHECK: (_2.0: Foo) = Foo::A; + // CHECK: (_2.0: Foo) = Foo::B; + // CHECK: _0 = copy (_2.0: Foo); + mir! { + let u: U; + let d: u8; + { + u.b = init; + d = Discriminant(u.b); + match d { + 0 => bb_a, + 1 => bb_b, + _ => bb_unreachable, + } + } + bb_unreachable = { + Unreachable() + } + bb_a = { + u.a = Foo::A; + Goto(bb_join) + } + bb_b = { + u.a = Foo::B; + Goto(bb_join) + } + bb_join = { + RET = u.a; + Return() + } + } +} + +fn main() { + let r = aliasing_locals(std::hint::black_box(Foo::B)); + assert!(r == Foo::B); + + let r = union_fields(std::hint::black_box(Foo::B)); + assert!(r == Foo::B); +} diff --git a/tests/mir-opt/match_branch_simplification_aliasing.union_fields.MatchBranchSimplification.diff b/tests/mir-opt/match_branch_simplification_aliasing.union_fields.MatchBranchSimplification.diff new file mode 100644 index 0000000000000..15092edd549ef --- /dev/null +++ b/tests/mir-opt/match_branch_simplification_aliasing.union_fields.MatchBranchSimplification.diff @@ -0,0 +1,34 @@ +- // MIR for `union_fields` before MatchBranchSimplification ++ // MIR for `union_fields` after MatchBranchSimplification + + fn union_fields(_1: Foo) -> Foo { + let mut _0: Foo; + let mut _2: U; + let mut _3: u8; + + bb0: { + (_2.1: Foo) = copy _1; + _3 = discriminant((_2.1: Foo)); + switchInt(copy _3) -> [0: bb2, 1: bb3, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + (_2.0: Foo) = Foo::A; + goto -> bb4; + } + + bb3: { + (_2.0: Foo) = Foo::B; + goto -> bb4; + } + + bb4: { + _0 = copy (_2.0: Foo); + return; + } + } + 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-fulldeps/rustc_public/check_abi.rs b/tests/ui-fulldeps/rustc_public/check_abi.rs index f6c95fb745409..92312cd4c8712 100644 --- a/tests/ui-fulldeps/rustc_public/check_abi.rs +++ b/tests/ui-fulldeps/rustc_public/check_abi.rs @@ -15,8 +15,8 @@ extern crate rustc_middle; extern crate rustc_public; use rustc_public::abi::{ - ArgAbi, ArgExtension, CallConvention, FieldsShape, IntegerLength, PassMode, Primitive, Scalar, - ValueRepr, VariantsShape, + ArgAbi, ArgExtension, CallConvention, FieldsShape, IndirectMode, IntegerLength, PassMode, + Primitive, Scalar, ValueRepr, VariantsShape, }; use rustc_public::mir::MirVisitor; use rustc_public::mir::mono::Instance; @@ -122,14 +122,14 @@ fn check_primitive(abi: &ArgAbi) { /// Check the return value: `Result`. fn check_result(abi: &ArgAbi) { assert!(abi.ty.kind().is_enum()); - let PassMode::Indirect { ref attrs, ref meta_attrs, on_stack } = abi.mode else { + let PassMode::Indirect { ref attrs, ref meta_attrs, address_space: _, mode } = abi.mode else { panic!("Expected PassMode::Indirect for Result, got: {:?}", abi.mode); }; // Indirect arguments have a pointee alignment (the pointer must be aligned). assert!(attrs.pointee_align().is_some()); // Result is a sized type, so no metadata pointer. assert!(meta_attrs.is_none()); - assert!(!on_stack); + assert!(mode == IndirectMode::Pointer); let layout = abi.layout.shape(); assert!(layout.is_sized()); assert_matches!(layout.fields, FieldsShape::Arbitrary { .. }); diff --git a/tests/ui-fulldeps/rustc_public/check_abi_cast.rs b/tests/ui-fulldeps/rustc_public/check_abi_cast.rs index 0bd4ac684066e..a54abdd5deeaf 100644 --- a/tests/ui-fulldeps/rustc_public/check_abi_cast.rs +++ b/tests/ui-fulldeps/rustc_public/check_abi_cast.rs @@ -23,7 +23,7 @@ use std::convert::TryFrom; use std::io::Write; use std::ops::ControlFlow; -use rustc_public::abi::{CallConvention, PassMode, RegKind}; +use rustc_public::abi::{CallConvention, IndirectMode, PassMode, RegKind}; use rustc_public::mir::mono::Instance; use rustc_public::{CrateDef, ItemKind}; @@ -147,7 +147,7 @@ fn test_abi_cast() -> ControlFlow<()> { } // Fourth TwoWords has no registers left → Indirect (on stack) assert!( - matches!(&abi.args[3].mode, PassMode::Indirect { on_stack: true, .. }), + matches!(&abi.args[3].mode, PassMode::Indirect { mode: IndirectMode::OnStack, .. }), "Expected arg 3 to be Indirect on stack, got: {:?}", abi.args[3].mode ); diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr index edea2d5772280..cefc2ba085182 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -35,7 +35,8 @@ error: fn_abi_of(pass_zst) = FnAbi { ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr index edea2d5772280..cefc2ba085182 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -35,7 +35,8 @@ error: fn_abi_of(pass_zst) = FnAbi { ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr index edea2d5772280..cefc2ba085182 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -35,7 +35,8 @@ error: fn_abi_of(pass_zst) = FnAbi { ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], diff --git a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr index edea2d5772280..cefc2ba085182 100644 --- a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr +++ b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr @@ -35,7 +35,8 @@ error: fn_abi_of(pass_zst) = FnAbi { ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index 6242d93b09534..1793674fa462a 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -446,7 +446,8 @@ error: ABIs are not compatible ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], @@ -519,7 +520,8 @@ error: ABIs are not compatible ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 176c68ecd4c7b..29ec7846101f1 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -446,7 +446,8 @@ error: ABIs are not compatible ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], @@ -519,7 +520,8 @@ error: ABIs are not compatible ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 176c68ecd4c7b..29ec7846101f1 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -446,7 +446,8 @@ error: ABIs are not compatible ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], @@ -519,7 +520,8 @@ error: ABIs are not compatible ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], diff --git a/tests/ui/abi/pass-indirectly-attr.rs b/tests/ui/abi/pass-indirectly-attr.rs index 54aafc716587c..bb90b8354ea91 100644 --- a/tests/ui/abi/pass-indirectly-attr.rs +++ b/tests/ui/abi/pass-indirectly-attr.rs @@ -20,7 +20,7 @@ pub struct Type(u8); pub extern "C" fn extern_c(_: Type) {} //~^ ERROR fn_abi_of(extern_c) = FnAbi { //~| ERROR mode: Indirect -//~| ERROR on_stack: false, +//~| ERROR mode: Pointer, //~| ERROR conv: C, #[rustc_abi(debug)] diff --git a/tests/ui/abi/pass-indirectly-attr.stderr b/tests/ui/abi/pass-indirectly-attr.stderr index efeec0d86982b..5821e6279bb85 100644 --- a/tests/ui/abi/pass-indirectly-attr.stderr +++ b/tests/ui/abi/pass-indirectly-attr.stderr @@ -48,7 +48,8 @@ error: fn_abi_of(extern_c) = FnAbi { ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], diff --git a/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr b/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr index 19b63a041d616..940a513cd4e4d 100644 --- a/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr +++ b/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr @@ -2,7 +2,6 @@ error[E0158]: constant pattern cannot depend on generic parameters --> $DIR/associated-const-type-parameter-pattern.rs:20:9 | LL | pub trait Foo { - | ------------- LL | const X: EFoo; | ------------- constant defined here ... @@ -16,7 +15,6 @@ error[E0158]: constant pattern cannot depend on generic parameters --> $DIR/associated-const-type-parameter-pattern.rs:22:9 | LL | pub trait Foo { - | ------------- LL | const X: EFoo; | ------------- constant defined here ... @@ -30,7 +28,6 @@ error[E0158]: constant pattern cannot depend on generic parameters --> $DIR/associated-const-type-parameter-pattern.rs:28:48 | LL | pub trait Foo { - | ------------- LL | const X: EFoo; | ------------- constant defined here ... @@ -43,7 +40,6 @@ error[E0158]: constant pattern cannot depend on generic parameters --> $DIR/associated-const-type-parameter-pattern.rs:30:9 | LL | pub trait Foo { - | ------------- LL | const X: EFoo; | ------------- constant defined here ... diff --git a/tests/ui/async-await/in-trait/generics-mismatch.stderr b/tests/ui/async-await/in-trait/generics-mismatch.stderr index cb0f95e8d098b..7c6f665c4db07 100644 --- a/tests/ui/async-await/in-trait/generics-mismatch.stderr +++ b/tests/ui/async-await/in-trait/generics-mismatch.stderr @@ -2,12 +2,10 @@ error[E0053]: associated function `foo` has an incompatible generic parameter fo --> $DIR/generics-mismatch.rs:8:18 | LL | trait Foo { - | --- LL | async fn foo(); | - expected type parameter ... LL | impl Foo for () { - | --------------- LL | async fn foo() {} | ^^^^^^^^^^^^^^ found const parameter of type `usize` 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/borrowck/borrow-immutable-upvar-mutation-impl-trait.stderr b/tests/ui/borrowck/borrow-immutable-upvar-mutation-impl-trait.stderr index 2b16206cd7742..76d304bb1ddd2 100644 --- a/tests/ui/borrowck/borrow-immutable-upvar-mutation-impl-trait.stderr +++ b/tests/ui/borrowck/borrow-immutable-upvar-mutation-impl-trait.stderr @@ -2,7 +2,7 @@ error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closu --> $DIR/borrow-immutable-upvar-mutation-impl-trait.rs:11:9 | LL | fn bar() -> impl Fn() -> usize { - | --- ------------------ change this to return `FnMut` instead of `Fn` + | ------------------ change this to return `FnMut` instead of `Fn` LL | let mut x = 0; LL | move || { | ------- in this closure diff --git a/tests/ui/borrowck/borrow-immutable-upvar-mutation.stderr b/tests/ui/borrowck/borrow-immutable-upvar-mutation.stderr index 4e40ebf738e3b..845e1497b06fd 100644 --- a/tests/ui/borrowck/borrow-immutable-upvar-mutation.stderr +++ b/tests/ui/borrowck/borrow-immutable-upvar-mutation.stderr @@ -81,7 +81,7 @@ error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closu --> $DIR/borrow-immutable-upvar-mutation.rs:53:9 | LL | fn foo() -> Box usize> { - | --- ---------------------- change this to return `FnMut` instead of `Fn` + | ---------------------- change this to return `FnMut` instead of `Fn` LL | let mut x = 0; LL | Box::new(move || { | ------- in this closure diff --git a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr index 45edd7bc0e0ee..c9e77ac941901 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr @@ -35,7 +35,8 @@ error: fn_abi_of(take_va_list) = FnAbi { ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], diff --git a/tests/ui/c-variadic/pass-by-value-abi.rs b/tests/ui/c-variadic/pass-by-value-abi.rs index bcca09e90438a..317840601c050 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.rs +++ b/tests/ui/c-variadic/pass-by-value-abi.rs @@ -27,9 +27,9 @@ use std::ffi::VaList; pub extern "C" fn take_va_list(_: VaList<'_>) {} //~^ ERROR fn_abi_of(take_va_list) = FnAbi { //[x86_64]~^^ ERROR mode: Indirect { -//[x86_64]~^^^ ERROR on_stack: false, +//[x86_64]~^^^ ERROR mode: Pointer, //[aarch64]~^^^^ ERROR mode: Indirect { -//[aarch64]~^^^^^ ERROR on_stack: false, +//[aarch64]~^^^^^ ERROR mode: Pointer, //[win]~^^^^^^ ERROR mode: Direct( #[cfg(all(target_arch = "x86_64", not(windows)))] @@ -37,11 +37,11 @@ pub extern "C" fn take_va_list(_: VaList<'_>) {} pub extern "sysv64" fn take_va_list_sysv64(_: VaList<'_>) {} //[x86_64]~^ ERROR fn_abi_of(take_va_list_sysv64) = FnAbi { //[x86_64]~^^ ERROR mode: Indirect { -//[x86_64]~^^^ ERROR on_stack: false, +//[x86_64]~^^^ ERROR mode: Pointer, #[cfg(all(target_arch = "x86_64", not(windows)))] #[rustc_abi(debug)] pub extern "win64" fn take_va_list_win64(_: VaList<'_>) {} //[x86_64]~^ ERROR: fn_abi_of(take_va_list_win64) = FnAbi { //[x86_64]~^^ ERROR mode: Indirect { -//[x86_64]~^^^ ERROR on_stack: false, +//[x86_64]~^^^ ERROR mode: Pointer, diff --git a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr index 1e203b93e66b3..04320a5312361 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr @@ -35,7 +35,8 @@ error: fn_abi_of(take_va_list) = FnAbi { ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], @@ -113,7 +114,8 @@ error: fn_abi_of(take_va_list_sysv64) = FnAbi { ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], @@ -193,7 +195,8 @@ error: fn_abi_of(take_va_list_win64) = FnAbi { ), }, meta_attrs: None, - on_stack: false, + address_space: None, + mode: Pointer, }, }, ], diff --git a/tests/ui/const-generics/ban-self-when-feature-not-enabled.stderr b/tests/ui/const-generics/ban-self-when-feature-not-enabled.stderr index 4961d79900375..4a74de9d5665f 100644 --- a/tests/ui/const-generics/ban-self-when-feature-not-enabled.stderr +++ b/tests/ui/const-generics/ban-self-when-feature-not-enabled.stderr @@ -10,12 +10,10 @@ error[E0053]: associated function `foo` has an incompatible generic parameter fo --> $DIR/ban-self-when-feature-not-enabled.rs:8:12 | LL | trait MyTrait { - | ------- LL | fn foo(); | ------------ expected const parameter of type `i32` ... LL | impl MyTrait for i32 { - | -------------------- LL | fn foo() {} | ^^^^^^^^^^^^^ found const parameter of type `{type error}` diff --git a/tests/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.stderr b/tests/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.stderr index 7ec162e1c295c..cb37701f9ad37 100644 --- a/tests/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.stderr +++ b/tests/ui/const-generics/defaults/mismatched_ty_const_in_trait_impl.stderr @@ -2,12 +2,10 @@ error[E0053]: associated function `foo` has an incompatible generic parameter fo --> $DIR/mismatched_ty_const_in_trait_impl.rs:5:12 | LL | trait Trait { - | ----- LL | fn foo() {} | - expected type parameter LL | } LL | impl Trait for () { - | ----------------- LL | fn foo() {} | ^^^^^^^^^^^^ found const parameter of type `u64` @@ -15,12 +13,10 @@ error[E0053]: associated function `bar` has an incompatible generic parameter fo --> $DIR/mismatched_ty_const_in_trait_impl.rs:13:12 | LL | trait Other { - | ----- LL | fn bar() {} | ----------- expected const parameter of type `u8` LL | } LL | impl Other for () { - | ----------------- LL | fn bar() {} | ^ found type parameter @@ -28,12 +24,10 @@ error[E0053]: associated function `baz` has an incompatible generic parameter fo --> $DIR/mismatched_ty_const_in_trait_impl.rs:21:12 | LL | trait Uwu { - | --- LL | fn baz() {} | ------------ expected const parameter of type `u32` LL | } LL | impl Uwu for () { - | --------------- LL | fn baz() {} | ^^^^^^^^^^^^ found const parameter of type `i32` @@ -41,12 +35,10 @@ error[E0053]: associated function `bbbb` has an incompatible generic parameter f --> $DIR/mismatched_ty_const_in_trait_impl.rs:29:13 | LL | trait Aaaaaa { - | ------ LL | fn bbbb() {} | ------------ expected const parameter of type `u32` LL | } LL | impl Aaaaaa for () { - | ------------------ LL | fn bbbb() {} | ^ found type parameter @@ -54,12 +46,10 @@ error[E0053]: associated function `abcd` has an incompatible generic parameter f --> $DIR/mismatched_ty_const_in_trait_impl.rs:37:13 | LL | trait Names { - | ----- LL | fn abcd() {} | - expected type parameter LL | } LL | impl Names for () { - | ----------------- LL | fn abcd() {} | ^^^^^^^^^^^^ found const parameter of type `u32` diff --git a/tests/ui/const-generics/issues/issue-86820.stderr b/tests/ui/const-generics/issues/issue-86820.stderr index 2928c1923b8b6..fda12fbda3af4 100644 --- a/tests/ui/const-generics/issues/issue-86820.stderr +++ b/tests/ui/const-generics/issues/issue-86820.stderr @@ -2,12 +2,10 @@ error[E0053]: method `bit` has an incompatible generic parameter for trait `Bits --> $DIR/issue-86820.rs:16:12 | LL | trait Bits { - | ---- LL | fn bit(self) -> bool; | ----------- expected const parameter of type `u8` ... LL | impl Bits for u8 { - | ---------------- LL | fn bit(self) -> bool { | ^^^^^^^^^^^^^^ found const parameter of type `usize` 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/const-generics/mgca/type_const_in_pattern_too_generic.stderr b/tests/ui/const-generics/mgca/type_const_in_pattern_too_generic.stderr index 73187fdefe38d..c9c012ff12eb7 100644 --- a/tests/ui/const-generics/mgca/type_const_in_pattern_too_generic.stderr +++ b/tests/ui/const-generics/mgca/type_const_in_pattern_too_generic.stderr @@ -2,7 +2,6 @@ error: could not evaluate constant pattern --> $DIR/type_const_in_pattern_too_generic.rs:10:12 | LL | trait Trait { - | ----------- LL | #[rustc_always_gca] LL | const ASSOC: usize; | ------------------ constant defined here diff --git a/tests/ui/consts/const_in_pattern/reject_non_structural.stderr b/tests/ui/consts/const_in_pattern/reject_non_structural.stderr index 402e4aa1e148a..221b698383774 100644 --- a/tests/ui/consts/const_in_pattern/reject_non_structural.stderr +++ b/tests/ui/consts/const_in_pattern/reject_non_structural.stderr @@ -159,7 +159,7 @@ LL | struct NoDerive; | --------------- `NoDerive` must be annotated with `#[derive(PartialEq)]` to be usable in patterns ... LL | trait Trait: Sized { const ASSOC: Option; } - | ------------------ ------------------------- constant defined here + | ------------------------- constant defined here LL | impl Trait for NoDerive { const ASSOC: Option = Some(NoDerive); } LL | match Some(NoDerive) { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; | ^^^^^^^^^^^^^^^ constant of non-structural type diff --git a/tests/ui/consts/issue-73976-polymorphic.stderr b/tests/ui/consts/issue-73976-polymorphic.stderr index 41a5e804c676d..871016607cc40 100644 --- a/tests/ui/consts/issue-73976-polymorphic.stderr +++ b/tests/ui/consts/issue-73976-polymorphic.stderr @@ -2,7 +2,6 @@ error[E0158]: constant pattern cannot depend on generic parameters --> $DIR/issue-73976-polymorphic.rs:19:37 | LL | impl GetTypeId { - | ----------------------------- LL | pub const VALUE: TypeId = TypeId::of::(); | ----------------------- constant defined here ... @@ -15,7 +14,6 @@ error[E0158]: constant pattern cannot depend on generic parameters --> $DIR/issue-73976-polymorphic.rs:30:42 | LL | impl GetTypeNameLen { - | ---------------------------------- LL | pub const VALUE: usize = any::type_name::().len(); | ---------------------- constant defined here ... diff --git a/tests/ui/consts/issue-79137-toogeneric.stderr b/tests/ui/consts/issue-79137-toogeneric.stderr index 33e32a7d15dca..f7b6a48f68123 100644 --- a/tests/ui/consts/issue-79137-toogeneric.stderr +++ b/tests/ui/consts/issue-79137-toogeneric.stderr @@ -2,7 +2,6 @@ error[E0158]: constant pattern cannot depend on generic parameters --> $DIR/issue-79137-toogeneric.rs:12:43 | LL | impl GetVariantCount { - | -------------------------- LL | pub const VALUE: usize = std::mem::variant_count::(); | ---------------------- constant defined here ... diff --git a/tests/ui/error-emitter/multiline-removal-suggestion.svg b/tests/ui/error-emitter/multiline-removal-suggestion.svg index 5c47481d4c178..e1a15eba86fc0 100644 --- a/tests/ui/error-emitter/multiline-removal-suggestion.svg +++ b/tests/ui/error-emitter/multiline-removal-suggestion.svg @@ -1,4 +1,4 @@ - +

{ - | ----- ... LL | const D: usize; | -------------- expected const parameter of type `usize` ... LL | impl

Trait

for () { - | ----------------------- ... LL | const D: u16 = N; | ^^^^^^^^^^^^ found const parameter of type `u16` diff --git a/tests/ui/methods/method-call-err-msg.stderr b/tests/ui/methods/method-call-err-msg.stderr index 95de40ff891be..c461c89abc350 100644 --- a/tests/ui/methods/method-call-err-msg.stderr +++ b/tests/ui/methods/method-call-err-msg.stderr @@ -50,14 +50,12 @@ LL | .two(0, /* isize */); error[E0599]: `Foo` is not an iterator --> $DIR/method-call-err-msg.rs:19:7 | -LL | pub struct Foo; - | -------------- method `take` not found for this struct because it doesn't satisfy `Foo: Iterator` +LL | pub struct Foo; + | -------------- method `take` not found for this struct because it doesn't satisfy `Foo: Iterator` ... -LL | / y.zero() -LL | | .take() - | | -^^^^ `Foo` is not an iterator - | |______| - | +LL | y.zero() +LL | .take() + | ^^^^ `Foo` is not an iterator | = note: the following trait bounds were not satisfied: `Foo: Iterator` diff --git a/tests/ui/moves/nested-loop-moved-value-wrong-continue.rs b/tests/ui/moves/nested-loop-moved-value-wrong-continue.rs index 0cbb501a57641..560cd2ac04cb9 100644 --- a/tests/ui/moves/nested-loop-moved-value-wrong-continue.rs +++ b/tests/ui/moves/nested-loop-moved-value-wrong-continue.rs @@ -7,8 +7,6 @@ fn foo() { //~^ NOTE this reinitialization might get skipped //~| NOTE move occurs because `foo` has type `String` //~| NOTE inside of this loop - //~| NOTE - //~| NOTE baz.push(foo); //~^ NOTE value moved here //~| HELP consider cloning the value @@ -30,17 +28,15 @@ fn main() { for foo in foos { //~^ NOTE this reinitialization might get skipped //~| NOTE move occurs because `foo` has type `String` - //~| NOTE for bar in &bars { //~^ NOTE inside of this loop - //~| NOTE if foo == *bar { baz.push(foo); //~^ NOTE value moved here //~| HELP consider cloning the value continue; //~^ NOTE verify that your loop breaking logic is correct - //~| NOTE this `continue` advances the loop at line 34 + //~| NOTE this `continue` advances the loop at line 31 } } qux.push(foo); diff --git a/tests/ui/moves/nested-loop-moved-value-wrong-continue.stderr b/tests/ui/moves/nested-loop-moved-value-wrong-continue.stderr index 60be70007fbe2..16cee6cf74f74 100644 --- a/tests/ui/moves/nested-loop-moved-value-wrong-continue.stderr +++ b/tests/ui/moves/nested-loop-moved-value-wrong-continue.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `foo` - --> $DIR/nested-loop-moved-value-wrong-continue.rs:19:14 + --> $DIR/nested-loop-moved-value-wrong-continue.rs:17:14 | LL | for foo in foos { for bar in &bars { if foo == *bar { | --- ---------------- inside of this loop @@ -14,20 +14,19 @@ LL | qux.push(foo); | ^^^ value used here after move | note: verify that your loop breaking logic is correct - --> $DIR/nested-loop-moved-value-wrong-continue.rs:15:9 + --> $DIR/nested-loop-moved-value-wrong-continue.rs:13:9 | LL | for foo in foos { for bar in &bars { if foo == *bar { - | --------------- ---------------- ... LL | continue; - | ^^^^^^^^ this `continue` advances the loop at $DIR/nested-loop-moved-value-wrong-continue.rs:6:23: 18:8 + | ^^^^^^^^ this `continue` advances the loop at $DIR/nested-loop-moved-value-wrong-continue.rs:6:23: 16:8 help: consider cloning the value if the performance cost is acceptable | LL | baz.push(foo.clone()); | ++++++++ error[E0382]: use of moved value: `foo` - --> $DIR/nested-loop-moved-value-wrong-continue.rs:46:18 + --> $DIR/nested-loop-moved-value-wrong-continue.rs:42:18 | LL | for foo in foos { | --- @@ -45,16 +44,14 @@ LL | qux.push(foo); | ^^^ value used here after move | note: verify that your loop breaking logic is correct - --> $DIR/nested-loop-moved-value-wrong-continue.rs:41:17 + --> $DIR/nested-loop-moved-value-wrong-continue.rs:37:17 | LL | for foo in foos { - | --------------- ... LL | for bar in &bars { - | ---------------- ... LL | continue; - | ^^^^^^^^ this `continue` advances the loop at line 34 + | ^^^^^^^^ this `continue` advances the loop at line 31 help: consider cloning the value if the performance cost is acceptable | LL | baz.push(foo.clone()); diff --git a/tests/ui/moves/recreating-value-in-loop-condition.stderr b/tests/ui/moves/recreating-value-in-loop-condition.stderr index 75c9633bc0ae8..dfdf5239f7a41 100644 --- a/tests/ui/moves/recreating-value-in-loop-condition.stderr +++ b/tests/ui/moves/recreating-value-in-loop-condition.stderr @@ -130,15 +130,11 @@ note: verify that your loop breaking logic is correct --> $DIR/recreating-value-in-loop-condition.rs:52:25 | LL | loop { - | ---- LL | let vec = vec!["one", "two", "three"]; LL | loop { - | ---- LL | LL | loop { - | ---- LL | loop { - | ---- ... LL | break; | ^^^^^ this `break` exits the loop at line 49 diff --git a/tests/ui/pattern/generic-in-path.stderr b/tests/ui/pattern/generic-in-path.stderr index 4f522626e52fe..011ce02a6f8b2 100644 --- a/tests/ui/pattern/generic-in-path.stderr +++ b/tests/ui/pattern/generic-in-path.stderr @@ -2,7 +2,6 @@ error[E0158]: constant pattern cannot depend on generic parameters --> $DIR/generic-in-path.rs:12:9 | LL | impl Thing { - | ----------------------------- LL | const A: Self = Thing; | ------------- constant defined here ... diff --git a/tests/ui/pattern/non-structural-match-types.stderr b/tests/ui/pattern/non-structural-match-types.stderr index 3b74ffe7cb7f4..b011d6424e8aa 100644 --- a/tests/ui/pattern/non-structural-match-types.stderr +++ b/tests/ui/pattern/non-structural-match-types.stderr @@ -2,7 +2,6 @@ error: constant of non-structural type `Option` in a pattern --> $DIR/non-structural-match-types.rs:10:9 | LL | impl AnyOption { - | -------------------- LL | const NONE: Option = None; | --------------------- constant defined here ... @@ -15,7 +14,6 @@ error: constant of non-structural type `Option<{closure@$DIR/non-structural-matc --> $DIR/non-structural-match-types.rs:16:9 | LL | impl AnyOption { - | -------------------- LL | const NONE: Option = None; | --------------------- constant defined here ... diff --git a/tests/ui/pattern/pat-tuple-overfield.stderr b/tests/ui/pattern/pat-tuple-overfield.stderr index 0dc16bfb99f12..151eee506d801 100644 --- a/tests/ui/pattern/pat-tuple-overfield.stderr +++ b/tests/ui/pattern/pat-tuple-overfield.stderr @@ -261,7 +261,7 @@ LL | u8, | -- tuple struct has 5 fields ... LL | M(1, - | - ^ + | ^ LL | 2, | ^ LL | 3, @@ -290,7 +290,6 @@ LL | u8, | -- tuple struct has 5 fields ... LL | M( - | - LL | 1, | ^ LL | 2, diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs index 4a7b2c956fc3c..07e12932eb902 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs +++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs @@ -9,12 +9,9 @@ enum E { //~^ NOTE `E` defined here //~| NOTE `E` defined here //~| NOTE `E` defined here - //~| NOTE - //~| NOTE - //~| NOTE - //~| NOTE - //~| NOTE - //~| NOTE + //~| NOTE `E` defined here + //~| NOTE `E` defined here + //~| NOTE `E` defined here A, B, //~^ NOTE not covered @@ -82,8 +79,7 @@ fn by_ref_thrice(e: & &mut &E) { enum Opt { //~^ NOTE `Opt` defined here - //~| NOTE - //~| NOTE + //~| NOTE `Opt` defined here Some(u8), None, //~^ NOTE not covered diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr index d31510d66e0c9..1684430271416 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr +++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `E::B` and `E::C` not covered - --> $DIR/non-exhaustive-defined-here.rs:37:11 + --> $DIR/non-exhaustive-defined-here.rs:34:11 | LL | match e1 { | ^^ patterns `E::B` and `E::C` not covered @@ -23,7 +23,7 @@ LL + E::B | E::C => todo!() | error[E0005]: refutable pattern in local binding - --> $DIR/non-exhaustive-defined-here.rs:43:9 + --> $DIR/non-exhaustive-defined-here.rs:40:9 | LL | let E::A = e; | ^^^^ patterns `E::B` and `E::C` not covered @@ -48,7 +48,7 @@ LL | if let E::A = e { todo!() }; | ++ +++++++++++ error[E0004]: non-exhaustive patterns: `&E::B` and `&E::C` not covered - --> $DIR/non-exhaustive-defined-here.rs:52:11 + --> $DIR/non-exhaustive-defined-here.rs:49:11 | LL | match e { | ^ patterns `&E::B` and `&E::C` not covered @@ -72,7 +72,7 @@ LL + &E::B | &E::C => todo!() | error[E0005]: refutable pattern in local binding - --> $DIR/non-exhaustive-defined-here.rs:59:9 + --> $DIR/non-exhaustive-defined-here.rs:56:9 | LL | let E::A = e; | ^^^^ patterns `&E::B` and `&E::C` not covered @@ -97,7 +97,7 @@ LL | if let E::A = e { todo!() }; | ++ +++++++++++ error[E0004]: non-exhaustive patterns: `&&mut &E::B` and `&&mut &E::C` not covered - --> $DIR/non-exhaustive-defined-here.rs:68:11 + --> $DIR/non-exhaustive-defined-here.rs:65:11 | LL | match e { | ^ patterns `&&mut &E::B` and `&&mut &E::C` not covered @@ -121,7 +121,7 @@ LL + &&mut &E::B | &&mut &E::C => todo!() | error[E0005]: refutable pattern in local binding - --> $DIR/non-exhaustive-defined-here.rs:75:9 + --> $DIR/non-exhaustive-defined-here.rs:72:9 | LL | let E::A = e; | ^^^^ patterns `&&mut &E::B` and `&&mut &E::C` not covered @@ -146,13 +146,13 @@ LL | if let E::A = e { todo!() }; | ++ +++++++++++ error[E0004]: non-exhaustive patterns: `Opt::None` not covered - --> $DIR/non-exhaustive-defined-here.rs:94:11 + --> $DIR/non-exhaustive-defined-here.rs:90:11 | LL | match e { | ^ pattern `Opt::None` not covered | note: `Opt` defined here - --> $DIR/non-exhaustive-defined-here.rs:83:6 + --> $DIR/non-exhaustive-defined-here.rs:80:6 | LL | enum Opt { | ^^^ @@ -167,7 +167,7 @@ LL + Opt::None => todo!() | error[E0005]: refutable pattern in local binding - --> $DIR/non-exhaustive-defined-here.rs:101:9 + --> $DIR/non-exhaustive-defined-here.rs:97:9 | LL | let Opt::Some(ref _x) = e; | ^^^^^^^^^^^^^^^^^ pattern `Opt::None` not covered @@ -175,7 +175,7 @@ LL | let Opt::Some(ref _x) = e; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html note: `Opt` defined here - --> $DIR/non-exhaustive-defined-here.rs:83:6 + --> $DIR/non-exhaustive-defined-here.rs:80:6 | LL | enum Opt { | ^^^ diff --git a/tests/ui/self/arbitrary-self-types-dyn-receiver.rs b/tests/ui/self/arbitrary-self-types-dyn-receiver.rs index fe128301d497a..3be08611c9e22 100644 --- a/tests/ui/self/arbitrary-self-types-dyn-receiver.rs +++ b/tests/ui/self/arbitrary-self-types-dyn-receiver.rs @@ -1,15 +1,20 @@ -//@ run-pass +//@ check-fail #![feature(arbitrary_self_types)] use std::ops::Receiver; trait Trait { fn foo(self: &dyn Receiver); + //~^ ERROR: the trait `std::ops::Receiver` is not dyn compatible + //~| ERROR: the trait `std::ops::Receiver` is not dyn compatible } struct Thing; impl Trait for Thing { fn foo(self: &dyn Receiver) { + //~^ ERROR: the trait `std::ops::Receiver` is not dyn compatible + //~| ERROR: the trait `std::ops::Receiver` is not dyn compatible + //~| ERROR: the trait `std::ops::Receiver` is not dyn compatible println!("huh???"); } } @@ -17,5 +22,6 @@ impl Trait for Thing { fn main() { let x = Box::new(Thing); let y: &dyn Receiver = &x; + //~^ ERROR: the trait `std::ops::Receiver` is not dyn compatible y.foo(); } diff --git a/tests/ui/self/arbitrary-self-types-dyn-receiver.stderr b/tests/ui/self/arbitrary-self-types-dyn-receiver.stderr new file mode 100644 index 0000000000000..24acb56ffb1b3 --- /dev/null +++ b/tests/ui/self/arbitrary-self-types-dyn-receiver.stderr @@ -0,0 +1,85 @@ +error[E0038]: the trait `std::ops::Receiver` is not dyn compatible + --> $DIR/arbitrary-self-types-dyn-receiver.rs:14:5 + | +LL | fn foo(self: &dyn Receiver) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::ops::Receiver` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $SRC_DIR/core/src/ops/deref.rs:LL:COL + | + = note: the trait is not dyn compatible because it opted out of dyn-compatibility + +error[E0038]: the trait `std::ops::Receiver` is not dyn compatible + --> $DIR/arbitrary-self-types-dyn-receiver.rs:14:18 + | +LL | fn foo(self: &dyn Receiver) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::ops::Receiver` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $SRC_DIR/core/src/ops/deref.rs:LL:COL + | + = note: the trait is not dyn compatible because it opted out of dyn-compatibility + +error[E0038]: the trait `std::ops::Receiver` is not dyn compatible + --> $DIR/arbitrary-self-types-dyn-receiver.rs:14:19 + | +LL | fn foo(self: &dyn Receiver) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ `std::ops::Receiver` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $SRC_DIR/core/src/ops/deref.rs:LL:COL + | + = note: the trait is not dyn compatible because it opted out of dyn-compatibility +help: you might have meant to use `Self` to refer to the implementing type + | +LL - fn foo(self: &dyn Receiver) { +LL + fn foo(self: &Self) { + | + +error[E0038]: the trait `std::ops::Receiver` is not dyn compatible + --> $DIR/arbitrary-self-types-dyn-receiver.rs:7:18 + | +LL | fn foo(self: &dyn Receiver); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::ops::Receiver` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $SRC_DIR/core/src/ops/deref.rs:LL:COL + | + = note: the trait is not dyn compatible because it opted out of dyn-compatibility + +error[E0038]: the trait `std::ops::Receiver` is not dyn compatible + --> $DIR/arbitrary-self-types-dyn-receiver.rs:7:19 + | +LL | fn foo(self: &dyn Receiver); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ `std::ops::Receiver` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $SRC_DIR/core/src/ops/deref.rs:LL:COL + | + = note: the trait is not dyn compatible because it opted out of dyn-compatibility +help: you might have meant to use `Self` to refer to the implementing type + | +LL - fn foo(self: &dyn Receiver); +LL + fn foo(self: &Self); + | + +error[E0038]: the trait `std::ops::Receiver` is not dyn compatible + --> $DIR/arbitrary-self-types-dyn-receiver.rs:24:17 + | +LL | let y: &dyn Receiver = &x; + | ^^^^^^^^^^^^^^^^^^^^^^ `std::ops::Receiver` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $SRC_DIR/core/src/ops/deref.rs:LL:COL + | + = note: the trait is not dyn compatible because it opted out of dyn-compatibility + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0038`. 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() {} diff --git a/tests/ui/typeck/issue-31173.stderr b/tests/ui/typeck/issue-31173.stderr index 5815da4dea9cb..d3935c51d1880 100644 --- a/tests/ui/typeck/issue-31173.stderr +++ b/tests/ui/typeck/issue-31173.stderr @@ -24,17 +24,14 @@ note: required by a bound in `cloned` error[E0599]: the method `collect` exists for struct `Cloned, {closure@$DIR/issue-31173.rs:7:21: 7:25}>>`, but its trait bounds were not satisfied --> $DIR/issue-31173.rs:12:10 | -LL | let temp: Vec = it - | _________________________- -LL | | .take_while(|&x| { -LL | | found_e = true; -LL | | false -LL | | }) -LL | | .cloned() -LL | | .collect(); - | | -^^^^^^^ method cannot be called due to unsatisfied trait bounds - | |_________| - | +LL | let temp: Vec = it +LL | .take_while(|&x| { +LL | found_e = true; +LL | false +LL | }) +LL | .cloned() +LL | .collect(); + | ^^^^^^^ method cannot be called due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: `, {closure@$DIR/issue-31173.rs:7:21: 7:25}> as Iterator>::Item = &_`