From 1f41fbe0ef6685a97d9d04056c2de3020aa81775 Mon Sep 17 00:00:00 2001 From: Boxy Date: Mon, 1 Jul 2024 20:39:48 +0100 Subject: [PATCH 01/10] introduce tests --- tests/ui/dropck/const_drop_is_valid.rs | 11 + tests/ui/dropck/const_drop_is_valid.stderr | 45 ++++ .../constrained_by_assoc_type_equality.rs | 12 + .../constrained_by_assoc_type_equality.stderr | 15 ++ .../unconstrained_const_param_on_drop.rs | 8 + .../unconstrained_const_param_on_drop.stderr | 219 ++++++++++++++++++ 6 files changed, 310 insertions(+) create mode 100644 tests/ui/dropck/const_drop_is_valid.rs create mode 100644 tests/ui/dropck/const_drop_is_valid.stderr create mode 100644 tests/ui/dropck/constrained_by_assoc_type_equality.rs create mode 100644 tests/ui/dropck/constrained_by_assoc_type_equality.stderr create mode 100644 tests/ui/dropck/unconstrained_const_param_on_drop.rs create mode 100644 tests/ui/dropck/unconstrained_const_param_on_drop.stderr diff --git a/tests/ui/dropck/const_drop_is_valid.rs b/tests/ui/dropck/const_drop_is_valid.rs new file mode 100644 index 0000000000000..0441b6ed067a4 --- /dev/null +++ b/tests/ui/dropck/const_drop_is_valid.rs @@ -0,0 +1,11 @@ +#![feature(effects)] +//~^ WARN: the feature `effects` is incomplete + +struct A(); + +impl const Drop for A {} +//~^ ERROR: const trait impls are experimental +//~| const `impl` for trait `Drop` which is not marked with `#[const_trait]` +//~| not all trait items implemented, missing: `drop` + +fn main() {} diff --git a/tests/ui/dropck/const_drop_is_valid.stderr b/tests/ui/dropck/const_drop_is_valid.stderr new file mode 100644 index 0000000000000..f15b7ba946dbf --- /dev/null +++ b/tests/ui/dropck/const_drop_is_valid.stderr @@ -0,0 +1,45 @@ +error[E0658]: const trait impls are experimental + --> $DIR/const_drop_is_valid.rs:6:6 + | +LL | impl const Drop for A {} + | ^^^^^ + | + = note: see issue #67792 for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/const_drop_is_valid.rs:1:12 + | +LL | #![feature(effects)] + | ^^^^^^^ + | + = note: see issue #102090 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: using `#![feature(effects)]` without enabling next trait solver globally + | + = note: the next trait solver must be enabled globally for the effects feature to work correctly + = help: use `-Znext-solver` to enable + +error: const `impl` for trait `Drop` which is not marked with `#[const_trait]` + --> $DIR/const_drop_is_valid.rs:6:12 + | +LL | impl const Drop for A {} + | ^^^^ + | + = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` + = note: adding a non-const method body in the future would be a breaking change + +error[E0046]: not all trait items implemented, missing: `drop` + --> $DIR/const_drop_is_valid.rs:6:1 + | +LL | impl const Drop for A {} + | ^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation + | + = help: implement the missing item: `fn drop(&mut self) { todo!() }` + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0046, E0658. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/dropck/constrained_by_assoc_type_equality.rs b/tests/ui/dropck/constrained_by_assoc_type_equality.rs new file mode 100644 index 0000000000000..521806cc1d7ac --- /dev/null +++ b/tests/ui/dropck/constrained_by_assoc_type_equality.rs @@ -0,0 +1,12 @@ +struct Foo(T); + +trait Trait { + type Assoc; +} + +impl, U: ?Sized> Drop for Foo { + //~^ ERROR: `Drop` impl requires `::Assoc == U` + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/dropck/constrained_by_assoc_type_equality.stderr b/tests/ui/dropck/constrained_by_assoc_type_equality.stderr new file mode 100644 index 0000000000000..29f4881c3fbef --- /dev/null +++ b/tests/ui/dropck/constrained_by_assoc_type_equality.stderr @@ -0,0 +1,15 @@ +error[E0367]: `Drop` impl requires `::Assoc == U` but the struct it is implemented for does not + --> $DIR/constrained_by_assoc_type_equality.rs:7:15 + | +LL | impl, U: ?Sized> Drop for Foo { + | ^^^^^^^^^ + | +note: the implementor must specify the same requirement + --> $DIR/constrained_by_assoc_type_equality.rs:1:1 + | +LL | struct Foo(T); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0367`. diff --git a/tests/ui/dropck/unconstrained_const_param_on_drop.rs b/tests/ui/dropck/unconstrained_const_param_on_drop.rs new file mode 100644 index 0000000000000..369ec7280bd36 --- /dev/null +++ b/tests/ui/dropck/unconstrained_const_param_on_drop.rs @@ -0,0 +1,8 @@ +//@ known-bug: unknown +//@ failure-status: 101 + +struct Foo {} + +impl Drop for Foo {} + +fn main() {} diff --git a/tests/ui/dropck/unconstrained_const_param_on_drop.stderr b/tests/ui/dropck/unconstrained_const_param_on_drop.stderr new file mode 100644 index 0000000000000..b48ae8cc8af21 --- /dev/null +++ b/tests/ui/dropck/unconstrained_const_param_on_drop.stderr @@ -0,0 +1,219 @@ +thread 'rustc' panicked at compiler/rustc_middle/src/ty/sty.rs:360:36: +called `Option::unwrap()` on a `None` value +stack backtrace: + 0: begin_panic_handler + at ./library/std/src/panicking.rs:661:5 + 1: panic_fmt + at ./library/core/src/panicking.rs:74:14 + 2: panic + at ./library/core/src/panicking.rs:148:5 + 3: core::option::unwrap_failed + at ./library/core/src/option.rs:2013:5 + 4: unwrap + at ./library/core/src/option.rs:963:21 + 5: find_ty_from_env + at ./compiler/rustc_middle/src/ty/sty.rs:360:36 + 6: process_obligation + at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:472:29 + 7: process_obligations + at ./compiler/rustc_data_structures/src/obligation_forest/mod.rs:462:23 + 8: select + at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:107:13 + 9: select_where_possible + at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:160:9 + 10: select_all_or_error, rustc_trait_selection::traits::FulfillmentError> + at ./compiler/rustc_infer/src/traits/engine.rs:82:22 + 11: select_all_or_error + at ./compiler/rustc_trait_selection/src/traits/engine.rs:189:9 + 12: ensure_drop_predicates_are_implied_by_item_defn + at ./compiler/rustc_hir_analysis/src/check/dropck.rs:154:18 + 13: check_drop_impl + at ./compiler/rustc_hir_analysis/src/check/dropck.rs:60:13 + 14: call core::result::Result<(), rustc_span::ErrorGuaranteed>, (rustc_middle::ty::context::TyCtxt, rustc_span::def_id::DefId)> + at ./library/core/src/ops/function.rs:79:5 + 15: {closure#0} core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_middle/src/ty/util.rs:360:16 + 16: for_each_relevant_impl core::result::Result<(), rustc_span::ErrorGuaranteed>>> + at ./compiler/rustc_middle/src/ty/trait_def.rs:166:21 + 17: calculate_dtor core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_middle/src/ty/util.rs:359:9 + 18: rustc_hir_analysis::check::adt_destructor + at ./compiler/rustc_hir_analysis/src/check/mod.rs:125:5 + 19: {closure#0} + at ./compiler/rustc_query_impl/src/plumbing.rs:285:13 + [... omitted 22 frames ...] + 20: query_get_at>> + at ./compiler/rustc_middle/src/query/plumbing.rs:145:17 + 21: adt_destructor + at ./compiler/rustc_middle/src/query/plumbing.rs:423:31 + 22: adt_destructor + at ./compiler/rustc_middle/src/query/plumbing.rs:414:35 + 23: destructor + at ./compiler/rustc_middle/src/ty/adt.rs:610:13 + 24: check_struct + at ./compiler/rustc_hir_analysis/src/check/check.rs:71:5 + 25: check_item_type + at ./compiler/rustc_hir_analysis/src/check/check.rs:730:13 + 26: check_item + at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:335:5 + 27: check_well_formed + at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:192:39 + 28: {closure#0} + at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 + [... omitted 22 frames ...] + 29: query_ensure_error_guaranteed>, ()> + at ./compiler/rustc_middle/src/query/plumbing.rs:181:9 + 30: check_well_formed + at ./compiler/rustc_middle/src/query/plumbing.rs:199:9 + 31: {closure#1} + at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:1995:47 + 32: {closure#0} + at ./compiler/rustc_middle/src/hir/mod.rs:89:57 + 33: {closure#0}<&[rustc_hir::hir::ImplItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_impl_items::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:203:50 + 34: call_once, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure#0}::{closure_env#0}<&[rustc_hir::hir::ImplItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_impl_items::{closure_env#0}>> + at ./library/core/src/panic/unwind_safe.rs:272:9 + 35: do_call>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/std/src/panicking.rs:553:40 + 36: try, core::panic::unwind_safe::AssertUnwindSafe>>> + at ./library/std/src/panicking.rs:517:19 + 37: catch_unwind>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/std/src/panic.rs:350:14 + 38: run, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure#2}::{closure_env#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:28:9 + 39: {closure#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:206:46 + 40: {closure#0}<&rustc_hir::hir::ItemId, core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/core/src/iter/adapters/filter_map.rs:39:28 + 41: fold, core::iter::adapters::filter_map::filter_map_fold::{closure_env#0}<&rustc_hir::hir::ItemId, core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>>> + at ./library/core/src/slice/iter/macros.rs:232:27 + 42: fold, core::slice::iter::Iter, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/core/src/iter/adapters/filter_map.rs:148:9 + 43: {closure#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:206:73 + 44: parallel_guard, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure_env#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:44:15 + 45: try_par_for_each_in<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:199:9 + 46: par_items + at ./compiler/rustc_middle/src/hir/mod.rs:75:9 + 47: check_mod_type_wf + at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:1994:19 + 48: {closure#0} + at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 + [... omitted 22 frames ...] + 49: query_ensure_error_guaranteed>, ()> + at ./compiler/rustc_middle/src/query/plumbing.rs:181:9 + 50: check_mod_type_wf + at ./compiler/rustc_middle/src/query/plumbing.rs:199:9 + 51: {closure#0} + at ./compiler/rustc_hir_analysis/src/lib.rs:162:21 + 52: {closure#0} + at ./compiler/rustc_middle/src/hir/map/mod.rs:463:13 + 53: {closure#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:182:34 + 54: call_once<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure#0}::{closure#0}::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> + at ./library/core/src/panic/unwind_safe.rs:272:9 + 55: do_call>>, ()> + at ./library/std/src/panicking.rs:553:40 + 56: try<(), core::panic::unwind_safe::AssertUnwindSafe>>> + at ./library/std/src/panicking.rs:517:19 + 57: catch_unwind>>, ()> + at ./library/std/src/panic.rs:350:14 + 58: run<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure#0}::{closure#1}::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:28:9 + 59: {closure#1}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:186:21 + 60: for_each>> + at ./library/core/src/slice/iter/macros.rs:254:21 + 61: {closure#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:185:17 + 62: parallel_guard<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:44:15 + 63: par_for_each_in<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> + at ./compiler/rustc_data_structures/src/sync/parallel.rs:178:9 + 64: par_for_each_module + at ./compiler/rustc_middle/src/hir/map/mod.rs:462:9 + 65: {closure#0} + at ./compiler/rustc_hir_analysis/src/lib.rs:161:9 + 66: run<(), rustc_hir_analysis::check_crate::{closure_env#0}> + at ./compiler/rustc_data_structures/src/profiling.rs:754:9 + 67: time<(), rustc_hir_analysis::check_crate::{closure_env#0}> + at ./compiler/rustc_session/src/utils.rs:16:9 + 68: check_crate + at ./compiler/rustc_hir_analysis/src/lib.rs:160:5 + 69: run_required_analyses + at ./compiler/rustc_interface/src/passes.rs:784:5 + 70: analysis + at ./compiler/rustc_interface/src/passes.rs:823:5 + 71: {closure#0} + at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 + [... omitted 22 frames ...] + 72: query_get_at>> + at ./compiler/rustc_middle/src/query/plumbing.rs:145:17 + 73: analysis + at ./compiler/rustc_middle/src/query/plumbing.rs:423:31 + 74: analysis + at ./compiler/rustc_middle/src/query/plumbing.rs:414:35 + 75: {closure#5} + at ./compiler/rustc_driver_impl/src/lib.rs:445:52 + 76: {closure#1}> + at ./compiler/rustc_middle/src/ty/context.rs:1296:37 + 77: {closure#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_middle/src/ty/context/tls.rs:82:9 + 78: try_with, rustc_middle::ty::context::tls::enter_context::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/std/src/thread/local.rs:283:12 + 79: with, rustc_middle::ty::context::tls::enter_context::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./library/std/src/thread/local.rs:260:9 + 80: enter_context>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_middle/src/ty/context/tls.rs:79:9 + 81: enter> + at ./compiler/rustc_middle/src/ty/context.rs:1296:9 + 82: {closure#1} + at ./compiler/rustc_driver_impl/src/lib.rs:445:13 + 83: enter, rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_interface/src/queries.rs:202:19 + 84: {closure#0} + at ./compiler/rustc_driver_impl/src/lib.rs:389:22 + 85: {closure#1}, rustc_driver_impl::run_compiler::{closure_env#0}> + at ./compiler/rustc_interface/src/interface.rs:502:27 + 86: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_interface/src/util.rs:154:13 + 87: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_interface/src/util.rs:106:21 + 88: set, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at /home/boxy/.cargo/registry/src/index.crates.io-6f17d22bba15001f/scoped-tls-1.0.1/src/lib.rs:137:9 + 89: create_session_globals_then, rustc_interface::util::run_in_thread_with_globals::{closure#0}::{closure#0}::{closure_env#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>>> + at ./compiler/rustc_span/src/lib.rs:134:5 + 90: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> + at ./compiler/rustc_interface/src/util.rs:105:17 +note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. + +error: the compiler unexpectedly panicked. this is a bug. + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc 1.81.0-dev running on x86_64-unknown-linux-gnu + +note: compiler flags: -Z threads=1 -Z simulate-remapped-rust-src-base=/rustc/FAKE_PREFIX -Z translate-remapped-path-to-local-path=no -Z ignore-directory-in-diagnostics-source-blocks=/home/boxy/.cargo -Z ignore-directory-in-diagnostics-source-blocks=/media/Nyoomies/Repos/rust/vendor -C codegen-units=1 -Z ui-testing -Z deduplicate-diagnostics=no -Z write-long-types-to-disk=no -C strip=debuginfo -C prefer-dynamic -C rpath -C debuginfo=0 + +query stack during panic: +#0 [adt_destructor] computing `Drop` impl for `Foo` +#1 [check_well_formed] checking that `Foo` is well-formed +#2 [check_mod_type_wf] checking that types are well-formed in top-level module +#3 [analysis] running analysis passes on this crate +end of query stack +error[E0207]: the const parameter `UNUSED` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained_const_param_on_drop.rs:6:6 + | +LL | impl Drop for Foo {} + | ^^^^^^^^^^^^^^^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0207`. From 6a0000827671ab12837a5a3a692d5dd9c31def69 Mon Sep 17 00:00:00 2001 From: Boxy Date: Mon, 1 Jul 2024 21:43:00 +0100 Subject: [PATCH 02/10] Rewrite dropck --- .../rustc_hir_analysis/src/check/dropck.rs | 34 ++- .../constrained_by_assoc_type_equality.rs | 3 +- ...ined_by_assoc_type_equality_and_self_ty.rs | 12 + ...by_assoc_type_equality_and_self_ty.stderr} | 10 +- .../dropck/reject-specialized-drops-8142.rs | 202 ++++++++++------ .../reject-specialized-drops-8142.stderr | 133 +++++------ .../ui/dropck/transitive-outlives.bad.stderr | 9 +- tests/ui/dropck/transitive-outlives.rs | 2 +- .../unconstrained_const_param_on_drop.rs | 5 +- .../unconstrained_const_param_on_drop.stderr | 224 ++---------------- 10 files changed, 266 insertions(+), 368 deletions(-) create mode 100644 tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.rs rename tests/ui/dropck/{constrained_by_assoc_type_equality.stderr => constrained_by_assoc_type_equality_and_self_ty.stderr} (53%) diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index 1937144802554..06ec01484a412 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -6,10 +6,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCauseCode; +use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::util::CheckRegions; -use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{GenericArgsRef, Ty}; use rustc_trait_selection::regions::InferCtxtRegionExt; use rustc_trait_selection::traits::{self, ObligationCtxt}; @@ -115,8 +115,9 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( Err(err.emit()) } -/// Confirms that every predicate imposed by dtor_predicates is -/// implied by assuming the predicates attached to self_type_did. +/// Confirms that all predicates defined on the `Drop` impl (`drop_impl_def_id`) are able to be +/// proven from within `adt_def_id`'s environment. I.e. all the predicates on the impl are +/// implied by the ADT being well formed. fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( tcx: TyCtxt<'tcx>, drop_impl_def_id: LocalDefId, @@ -126,6 +127,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let infcx = tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let impl_span = tcx.def_span(drop_impl_def_id.to_def_id()); + // Take the param-env of the adt and instantiate the args that show up in // the implementation's self type. This gives us the assumptions that the // self ty of the implementation is allowed to know just from it being a @@ -135,14 +138,27 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // We don't need to normalize this param-env or anything, since we're only // instantiating it with free params, so no additional param-env normalization // can occur on top of what has been done in the param_env query itself. - let param_env = + // + // Note: Ideally instead of instantiating the `ParamEnv` with the arguments from the impl ty we + // could instead use identity args for the adt. Unfortunately this would cause any errors to + // reference the params from the ADT instead of from the impl which is bad UX. To resolve + // this we "rename" the ADT's params to be the impl's params which should not affect behaviour. + let impl_adt_ty = Ty::new_adt(tcx, tcx.adt_def(adt_def_id), adt_to_impl_args); + let adt_env = ty::EarlyBinder::bind(tcx.param_env(adt_def_id)).instantiate(tcx, adt_to_impl_args); - for (pred, span) in tcx.predicates_of(drop_impl_def_id).instantiate_identity(tcx) { + let fresh_impl_args = infcx.fresh_args_for_item(impl_span, drop_impl_def_id.to_def_id()); + let fresh_adt_ty = + tcx.impl_trait_ref(drop_impl_def_id).unwrap().instantiate(tcx, fresh_impl_args).self_ty(); + + ocx.eq(&ObligationCause::dummy_with_span(impl_span), adt_env, fresh_adt_ty, impl_adt_ty) + .unwrap(); + + for (clause, span) in tcx.predicates_of(drop_impl_def_id).instantiate(tcx, fresh_impl_args) { let normalize_cause = traits::ObligationCause::misc(span, adt_def_id); - let pred = ocx.normalize(&normalize_cause, param_env, pred); + let pred = ocx.normalize(&normalize_cause, adt_env, clause); let cause = traits::ObligationCause::new(span, adt_def_id, ObligationCauseCode::DropImpl); - ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, pred)); + ocx.register_obligation(traits::Obligation::new(tcx, cause, adt_env, pred)); } // All of the custom error reporting logic is to preserve parity with the old @@ -176,7 +192,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( return Err(guar.unwrap()); } - let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(param_env)); + let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(adt_env)); if !errors.is_empty() { let mut guar = None; for error in errors { diff --git a/tests/ui/dropck/constrained_by_assoc_type_equality.rs b/tests/ui/dropck/constrained_by_assoc_type_equality.rs index 521806cc1d7ac..4101fe83c3bea 100644 --- a/tests/ui/dropck/constrained_by_assoc_type_equality.rs +++ b/tests/ui/dropck/constrained_by_assoc_type_equality.rs @@ -1,3 +1,5 @@ +//@ check-pass + struct Foo(T); trait Trait { @@ -5,7 +7,6 @@ trait Trait { } impl, U: ?Sized> Drop for Foo { - //~^ ERROR: `Drop` impl requires `::Assoc == U` fn drop(&mut self) {} } diff --git a/tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.rs b/tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.rs new file mode 100644 index 0000000000000..3a85b86cb1fdd --- /dev/null +++ b/tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.rs @@ -0,0 +1,12 @@ +trait Trait { + type Assoc; +} + +struct Foo(T, U); + +impl, U: ?Sized> Drop for Foo { + //~^ ERROR: `Drop` impl requires `::Assoc == U` + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/dropck/constrained_by_assoc_type_equality.stderr b/tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.stderr similarity index 53% rename from tests/ui/dropck/constrained_by_assoc_type_equality.stderr rename to tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.stderr index 29f4881c3fbef..dab8c55d0e7f3 100644 --- a/tests/ui/dropck/constrained_by_assoc_type_equality.stderr +++ b/tests/ui/dropck/constrained_by_assoc_type_equality_and_self_ty.stderr @@ -1,14 +1,14 @@ error[E0367]: `Drop` impl requires `::Assoc == U` but the struct it is implemented for does not - --> $DIR/constrained_by_assoc_type_equality.rs:7:15 + --> $DIR/constrained_by_assoc_type_equality_and_self_ty.rs:7:15 | -LL | impl, U: ?Sized> Drop for Foo { +LL | impl, U: ?Sized> Drop for Foo { | ^^^^^^^^^ | note: the implementor must specify the same requirement - --> $DIR/constrained_by_assoc_type_equality.rs:1:1 + --> $DIR/constrained_by_assoc_type_equality_and_self_ty.rs:5:1 | -LL | struct Foo(T); - | ^^^^^^^^^^^^^^^^^^^^ +LL | struct Foo(T, U); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/dropck/reject-specialized-drops-8142.rs b/tests/ui/dropck/reject-specialized-drops-8142.rs index 7a3bbe7cb0932..1b73fe9a06550 100644 --- a/tests/ui/dropck/reject-specialized-drops-8142.rs +++ b/tests/ui/dropck/reject-specialized-drops-8142.rs @@ -1,75 +1,145 @@ // Issue 8142: Test that Drop impls cannot be specialized beyond the // predicates attached to the type definition itself. -trait Bound { fn foo(&self) { } } -struct K<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } -struct L<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } -struct M<'m> { x: &'m i8 } -struct N<'n> { x: &'n i8 } -struct O { x: *const To } -struct P { x: *const Tp } -struct Q { x: *const Tq } -struct R { x: *const Tr } -struct S { x: *const Ts } -struct T<'t,Ts:'t> { x: &'t Ts } +trait Bound { + fn foo(&self) {} +} +struct K<'l1, 'l2> { + x: &'l1 i8, + y: &'l2 u8, +} +struct L<'l1, 'l2> { + x: &'l1 i8, + y: &'l2 u8, +} +struct M<'m> { + x: &'m i8, +} +struct N<'n> { + x: &'n i8, +} +struct O { + x: *const To, +} +struct P { + x: *const Tp, +} +struct Q { + x: *const Tq, +} +struct R { + x: *const Tr, +} +struct S { + x: *const Ts, +} +struct T<'t, Ts: 't> { + x: &'t Ts, +} struct U; -struct V { x: *const Tva, y: *const Tvb } -struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 } +struct V { + x: *const Tva, + y: *const Tvb, +} +struct W<'l1, 'l2> { + x: &'l1 i8, + y: &'l2 u8, +} struct X; struct Y; -enum Enum { Variant(T) } +enum Enum { + Variant(T), +} struct TupleStruct(T); -union Union { f: T } +union Union { + f: T, +} -impl<'al,'adds_bnd:'al> Drop for K<'al,'adds_bnd> { // REJECT +impl<'al, 'adds_bnd: 'al> Drop for K<'al, 'adds_bnd> { //~^ ERROR `Drop` impl requires `'adds_bnd: 'al` - fn drop(&mut self) { } } - -impl<'al,'adds_bnd> Drop for L<'al,'adds_bnd> where 'adds_bnd:'al { // REJECT - //~^ ERROR `Drop` impl requires `'adds_bnd: 'al` - fn drop(&mut self) { } } - -impl<'ml> Drop for M<'ml> { fn drop(&mut self) { } } // ACCEPT - -impl Drop for N<'static> { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl Drop for O { fn drop(&mut self) { } } // ACCEPT - -impl Drop for P { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl Drop for Q { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impl requires `AddsBnd: Bound` - -impl<'rbnd,AddsRBnd:'rbnd> Drop for R { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impl requires `AddsRBnd: 'rbnd` - -impl Drop for S { fn drop(&mut self) { } } // ACCEPT - -impl<'t,Bt:'t> Drop for T<'t,Bt> { fn drop(&mut self) { } } // ACCEPT - -impl Drop for U { fn drop(&mut self) { } } // ACCEPT - -impl Drop for V { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl Drop for X<3> { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl Drop for Y { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impls cannot be specialized - -impl Drop for Enum { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impl requires `AddsBnd: Bound` - -impl Drop for TupleStruct { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impl requires `AddsBnd: Bound` - -impl Drop for Union { fn drop(&mut self) { } } // REJECT -//~^ ERROR `Drop` impl requires `AddsBnd: Bound` - -pub fn main() { } + fn drop(&mut self) {} +} + +impl<'al, 'adds_bnd> Drop for L<'al, 'adds_bnd> +//~^ ERROR `Drop` impl requires `'adds_bnd: 'al` +where + 'adds_bnd: 'al, +{ + fn drop(&mut self) {} +} + +impl<'ml> Drop for M<'ml> { + fn drop(&mut self) {} +} + +impl Drop for N<'static> { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl Drop for O { + fn drop(&mut self) {} +} + +impl Drop for P { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl Drop for Q { + //~^ ERROR `Drop` impl requires `AddsBnd: Bound` + fn drop(&mut self) {} +} + +impl<'rbnd, AddsRBnd: 'rbnd> Drop for R { + fn drop(&mut self) {} +} + +impl Drop for S { + fn drop(&mut self) {} +} + +impl<'t, Bt: 't> Drop for T<'t, Bt> { + fn drop(&mut self) {} +} + +impl Drop for U { + fn drop(&mut self) {} +} + +impl Drop for V { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl<'lw> Drop for W<'lw, 'lw> { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl Drop for X<3> { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl Drop for Y { + //~^ ERROR `Drop` impls cannot be specialized + fn drop(&mut self) {} +} + +impl Drop for Enum { + //~^ ERROR `Drop` impl requires `AddsBnd: Bound` + fn drop(&mut self) {} +} + +impl Drop for TupleStruct { + //~^ ERROR `Drop` impl requires `AddsBnd: Bound` + fn drop(&mut self) {} +} + +impl Drop for Union { + //~^ ERROR `Drop` impl requires `AddsBnd: Bound` + fn drop(&mut self) {} +} + +pub fn main() {} diff --git a/tests/ui/dropck/reject-specialized-drops-8142.stderr b/tests/ui/dropck/reject-specialized-drops-8142.stderr index cb48221c67a82..9c8b6d544639a 100644 --- a/tests/ui/dropck/reject-specialized-drops-8142.stderr +++ b/tests/ui/dropck/reject-specialized-drops-8142.stderr @@ -1,166 +1,157 @@ error[E0367]: `Drop` impl requires `'adds_bnd: 'al` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:24:20 + --> $DIR/reject-specialized-drops-8142.rs:58:1 | -LL | impl<'al,'adds_bnd:'al> Drop for K<'al,'adds_bnd> { // REJECT - | ^^^ +LL | impl<'al, 'adds_bnd: 'al> Drop for K<'al, 'adds_bnd> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:4:1 + --> $DIR/reject-specialized-drops-8142.rs:6:1 | -LL | struct K<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } - | ^^^^^^^^^^^^^^^^^ +LL | struct K<'l1, 'l2> { + | ^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `'adds_bnd: 'al` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:28:67 + --> $DIR/reject-specialized-drops-8142.rs:63:1 | -LL | impl<'al,'adds_bnd> Drop for L<'al,'adds_bnd> where 'adds_bnd:'al { // REJECT - | ^^^ +LL | / impl<'al, 'adds_bnd> Drop for L<'al, 'adds_bnd> +LL | | +LL | | where +LL | | 'adds_bnd: 'al, + | |___________________^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:5:1 + --> $DIR/reject-specialized-drops-8142.rs:10:1 | -LL | struct L<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 } - | ^^^^^^^^^^^^^^^^^ +LL | struct L<'l1, 'l2> { + | ^^^^^^^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:34:1 + --> $DIR/reject-specialized-drops-8142.rs:75:1 | -LL | impl Drop for N<'static> { fn drop(&mut self) { } } // REJECT - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Drop for N<'static> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `'static` is not a generic parameter note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:7:1 + --> $DIR/reject-specialized-drops-8142.rs:17:1 | -LL | struct N<'n> { x: &'n i8 } +LL | struct N<'n> { | ^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:39:1 + --> $DIR/reject-specialized-drops-8142.rs:84:1 | -LL | impl Drop for P { fn drop(&mut self) { } } // REJECT - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Drop for P { + | ^^^^^^^^^^^^^^^^^^^ | = note: `i8` is not a generic parameter note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:9:1 + --> $DIR/reject-specialized-drops-8142.rs:23:1 | -LL | struct P { x: *const Tp } +LL | struct P { | ^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:42:14 + --> $DIR/reject-specialized-drops-8142.rs:89:15 | -LL | impl Drop for Q { fn drop(&mut self) { } } // REJECT - | ^^^^^ +LL | impl Drop for Q { + | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:10:1 + --> $DIR/reject-specialized-drops-8142.rs:26:1 | -LL | struct Q { x: *const Tq } - | ^^^^^^^^^^^^ - -error[E0367]: `Drop` impl requires `AddsRBnd: 'rbnd` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:45:21 - | -LL | impl<'rbnd,AddsRBnd:'rbnd> Drop for R { fn drop(&mut self) { } } // REJECT - | ^^^^^ - | -note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:11:1 - | -LL | struct R { x: *const Tr } +LL | struct Q { | ^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:54:1 + --> $DIR/reject-specialized-drops-8142.rs:110:1 | -LL | impl Drop for V { fn drop(&mut self) { } } // REJECT - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Drop for V { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `One` is mentioned multiple times note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:15:1 + --> $DIR/reject-specialized-drops-8142.rs:39:1 | -LL | struct V { x: *const Tva, y: *const Tvb } +LL | struct V { | ^^^^^^^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:57:1 + --> $DIR/reject-specialized-drops-8142.rs:115:1 | -LL | impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJECT - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl<'lw> Drop for W<'lw, 'lw> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `'lw` is mentioned multiple times note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:16:1 + --> $DIR/reject-specialized-drops-8142.rs:43:1 | -LL | struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 } +LL | struct W<'l1, 'l2> { | ^^^^^^^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:60:1 + --> $DIR/reject-specialized-drops-8142.rs:120:1 | -LL | impl Drop for X<3> { fn drop(&mut self) { } } // REJECT - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Drop for X<3> { + | ^^^^^^^^^^^^^^^^^^ | = note: `3` is not a generic parameter note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:17:1 + --> $DIR/reject-specialized-drops-8142.rs:47:1 | LL | struct X; | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0366]: `Drop` impls cannot be specialized - --> $DIR/reject-specialized-drops-8142.rs:63:1 + --> $DIR/reject-specialized-drops-8142.rs:125:1 | -LL | impl Drop for Y { fn drop(&mut self) { } } // REJECT +LL | impl Drop for Y { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `Ca` is mentioned multiple times note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/reject-specialized-drops-8142.rs:18:1 + --> $DIR/reject-specialized-drops-8142.rs:48:1 | LL | struct Y; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the enum it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:66:14 + --> $DIR/reject-specialized-drops-8142.rs:130:15 | -LL | impl Drop for Enum { fn drop(&mut self) { } } // REJECT - | ^^^^^ +LL | impl Drop for Enum { + | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:20:1 + --> $DIR/reject-specialized-drops-8142.rs:50:1 | -LL | enum Enum { Variant(T) } +LL | enum Enum { | ^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the struct it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:69:14 + --> $DIR/reject-specialized-drops-8142.rs:135:15 | -LL | impl Drop for TupleStruct { fn drop(&mut self) { } } // REJECT - | ^^^^^ +LL | impl Drop for TupleStruct { + | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:21:1 + --> $DIR/reject-specialized-drops-8142.rs:53:1 | LL | struct TupleStruct(T); | ^^^^^^^^^^^^^^^^^^^^^ error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the union it is implemented for does not - --> $DIR/reject-specialized-drops-8142.rs:72:21 + --> $DIR/reject-specialized-drops-8142.rs:140:22 | -LL | impl Drop for Union { fn drop(&mut self) { } } // REJECT - | ^^^^^ +LL | impl Drop for Union { + | ^^^^^ | note: the implementor must specify the same requirement - --> $DIR/reject-specialized-drops-8142.rs:22:1 + --> $DIR/reject-specialized-drops-8142.rs:54:1 | -LL | union Union { f: T } +LL | union Union { | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0366, E0367. For more information about an error, try `rustc --explain E0366`. diff --git a/tests/ui/dropck/transitive-outlives.bad.stderr b/tests/ui/dropck/transitive-outlives.bad.stderr index 9ecc4841dce53..5b7968fce80c1 100644 --- a/tests/ui/dropck/transitive-outlives.bad.stderr +++ b/tests/ui/dropck/transitive-outlives.bad.stderr @@ -1,8 +1,11 @@ error[E0367]: `Drop` impl requires `'a: 'c` but the struct it is implemented for does not - --> $DIR/transitive-outlives.rs:20:9 + --> $DIR/transitive-outlives.rs:18:1 | -LL | 'a: 'c, - | ^^ +LL | / impl<'a, 'b, 'c> Drop for DropMe<'a, 'b, 'c> +LL | | +LL | | where +LL | | 'a: 'c, + | |___________^ | note: the implementor must specify the same requirement --> $DIR/transitive-outlives.rs:7:1 diff --git a/tests/ui/dropck/transitive-outlives.rs b/tests/ui/dropck/transitive-outlives.rs index e96ac6faae478..37c0a1ff5e271 100644 --- a/tests/ui/dropck/transitive-outlives.rs +++ b/tests/ui/dropck/transitive-outlives.rs @@ -16,9 +16,9 @@ where #[cfg(bad)] impl<'a, 'b, 'c> Drop for DropMe<'a, 'b, 'c> +//[bad]~^ ERROR `Drop` impl requires `'a: 'c` where 'a: 'c, - //[bad]~^ ERROR `Drop` impl requires `'a: 'c` { fn drop(&mut self) {} } diff --git a/tests/ui/dropck/unconstrained_const_param_on_drop.rs b/tests/ui/dropck/unconstrained_const_param_on_drop.rs index 369ec7280bd36..de77fa55fb280 100644 --- a/tests/ui/dropck/unconstrained_const_param_on_drop.rs +++ b/tests/ui/dropck/unconstrained_const_param_on_drop.rs @@ -1,8 +1,7 @@ -//@ known-bug: unknown -//@ failure-status: 101 - struct Foo {} impl Drop for Foo {} +//~^ ERROR: `Drop` impl requires `the constant `_` has type `usize`` +//~| ERROR: the const parameter `UNUSED` is not constrained by the impl trait, self type, or predicates fn main() {} diff --git a/tests/ui/dropck/unconstrained_const_param_on_drop.stderr b/tests/ui/dropck/unconstrained_const_param_on_drop.stderr index b48ae8cc8af21..851888534eeb2 100644 --- a/tests/ui/dropck/unconstrained_const_param_on_drop.stderr +++ b/tests/ui/dropck/unconstrained_const_param_on_drop.stderr @@ -1,212 +1,17 @@ -thread 'rustc' panicked at compiler/rustc_middle/src/ty/sty.rs:360:36: -called `Option::unwrap()` on a `None` value -stack backtrace: - 0: begin_panic_handler - at ./library/std/src/panicking.rs:661:5 - 1: panic_fmt - at ./library/core/src/panicking.rs:74:14 - 2: panic - at ./library/core/src/panicking.rs:148:5 - 3: core::option::unwrap_failed - at ./library/core/src/option.rs:2013:5 - 4: unwrap - at ./library/core/src/option.rs:963:21 - 5: find_ty_from_env - at ./compiler/rustc_middle/src/ty/sty.rs:360:36 - 6: process_obligation - at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:472:29 - 7: process_obligations - at ./compiler/rustc_data_structures/src/obligation_forest/mod.rs:462:23 - 8: select - at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:107:13 - 9: select_where_possible - at ./compiler/rustc_trait_selection/src/traits/fulfill.rs:160:9 - 10: select_all_or_error, rustc_trait_selection::traits::FulfillmentError> - at ./compiler/rustc_infer/src/traits/engine.rs:82:22 - 11: select_all_or_error - at ./compiler/rustc_trait_selection/src/traits/engine.rs:189:9 - 12: ensure_drop_predicates_are_implied_by_item_defn - at ./compiler/rustc_hir_analysis/src/check/dropck.rs:154:18 - 13: check_drop_impl - at ./compiler/rustc_hir_analysis/src/check/dropck.rs:60:13 - 14: call core::result::Result<(), rustc_span::ErrorGuaranteed>, (rustc_middle::ty::context::TyCtxt, rustc_span::def_id::DefId)> - at ./library/core/src/ops/function.rs:79:5 - 15: {closure#0} core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_middle/src/ty/util.rs:360:16 - 16: for_each_relevant_impl core::result::Result<(), rustc_span::ErrorGuaranteed>>> - at ./compiler/rustc_middle/src/ty/trait_def.rs:166:21 - 17: calculate_dtor core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_middle/src/ty/util.rs:359:9 - 18: rustc_hir_analysis::check::adt_destructor - at ./compiler/rustc_hir_analysis/src/check/mod.rs:125:5 - 19: {closure#0} - at ./compiler/rustc_query_impl/src/plumbing.rs:285:13 - [... omitted 22 frames ...] - 20: query_get_at>> - at ./compiler/rustc_middle/src/query/plumbing.rs:145:17 - 21: adt_destructor - at ./compiler/rustc_middle/src/query/plumbing.rs:423:31 - 22: adt_destructor - at ./compiler/rustc_middle/src/query/plumbing.rs:414:35 - 23: destructor - at ./compiler/rustc_middle/src/ty/adt.rs:610:13 - 24: check_struct - at ./compiler/rustc_hir_analysis/src/check/check.rs:71:5 - 25: check_item_type - at ./compiler/rustc_hir_analysis/src/check/check.rs:730:13 - 26: check_item - at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:335:5 - 27: check_well_formed - at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:192:39 - 28: {closure#0} - at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 - [... omitted 22 frames ...] - 29: query_ensure_error_guaranteed>, ()> - at ./compiler/rustc_middle/src/query/plumbing.rs:181:9 - 30: check_well_formed - at ./compiler/rustc_middle/src/query/plumbing.rs:199:9 - 31: {closure#1} - at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:1995:47 - 32: {closure#0} - at ./compiler/rustc_middle/src/hir/mod.rs:89:57 - 33: {closure#0}<&[rustc_hir::hir::ImplItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_impl_items::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:203:50 - 34: call_once, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure#0}::{closure_env#0}<&[rustc_hir::hir::ImplItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_impl_items::{closure_env#0}>> - at ./library/core/src/panic/unwind_safe.rs:272:9 - 35: do_call>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/std/src/panicking.rs:553:40 - 36: try, core::panic::unwind_safe::AssertUnwindSafe>>> - at ./library/std/src/panicking.rs:517:19 - 37: catch_unwind>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/std/src/panic.rs:350:14 - 38: run, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure#2}::{closure_env#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:28:9 - 39: {closure#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:206:46 - 40: {closure#0}<&rustc_hir::hir::ItemId, core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/core/src/iter/adapters/filter_map.rs:39:28 - 41: fold, core::iter::adapters::filter_map::filter_map_fold::{closure_env#0}<&rustc_hir::hir::ItemId, core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>>> - at ./library/core/src/slice/iter/macros.rs:232:27 - 42: fold, core::slice::iter::Iter, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure#0}::{closure_env#2}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>, fn(core::result::Result<(), rustc_span::ErrorGuaranteed>, core::result::Result<(), rustc_span::ErrorGuaranteed>) -> core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/core/src/iter/adapters/filter_map.rs:148:9 - 43: {closure#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:206:73 - 44: parallel_guard, rustc_data_structures::sync::parallel::enabled::try_par_for_each_in::{closure_env#0}<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}>> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:44:15 - 45: try_par_for_each_in<&[rustc_hir::hir::ItemId], rustc_span::ErrorGuaranteed, rustc_middle::hir::{impl#0}::par_items::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:199:9 - 46: par_items - at ./compiler/rustc_middle/src/hir/mod.rs:75:9 - 47: check_mod_type_wf - at ./compiler/rustc_hir_analysis/src/check/wfcheck.rs:1994:19 - 48: {closure#0} - at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 - [... omitted 22 frames ...] - 49: query_ensure_error_guaranteed>, ()> - at ./compiler/rustc_middle/src/query/plumbing.rs:181:9 - 50: check_mod_type_wf - at ./compiler/rustc_middle/src/query/plumbing.rs:199:9 - 51: {closure#0} - at ./compiler/rustc_hir_analysis/src/lib.rs:162:21 - 52: {closure#0} - at ./compiler/rustc_middle/src/hir/map/mod.rs:463:13 - 53: {closure#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:182:34 - 54: call_once<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure#0}::{closure#0}::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> - at ./library/core/src/panic/unwind_safe.rs:272:9 - 55: do_call>>, ()> - at ./library/std/src/panicking.rs:553:40 - 56: try<(), core::panic::unwind_safe::AssertUnwindSafe>>> - at ./library/std/src/panicking.rs:517:19 - 57: catch_unwind>>, ()> - at ./library/std/src/panic.rs:350:14 - 58: run<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure#0}::{closure#1}::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:28:9 - 59: {closure#1}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:186:21 - 60: for_each>> - at ./library/core/src/slice/iter/macros.rs:254:21 - 61: {closure#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:185:17 - 62: parallel_guard<(), rustc_data_structures::sync::parallel::enabled::par_for_each_in::{closure_env#0}<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}>> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:44:15 - 63: par_for_each_in<&rustc_hir::hir_id::OwnerId, &[rustc_hir::hir_id::OwnerId], rustc_middle::hir::map::{impl#3}::par_for_each_module::{closure_env#0}> - at ./compiler/rustc_data_structures/src/sync/parallel.rs:178:9 - 64: par_for_each_module - at ./compiler/rustc_middle/src/hir/map/mod.rs:462:9 - 65: {closure#0} - at ./compiler/rustc_hir_analysis/src/lib.rs:161:9 - 66: run<(), rustc_hir_analysis::check_crate::{closure_env#0}> - at ./compiler/rustc_data_structures/src/profiling.rs:754:9 - 67: time<(), rustc_hir_analysis::check_crate::{closure_env#0}> - at ./compiler/rustc_session/src/utils.rs:16:9 - 68: check_crate - at ./compiler/rustc_hir_analysis/src/lib.rs:160:5 - 69: run_required_analyses - at ./compiler/rustc_interface/src/passes.rs:784:5 - 70: analysis - at ./compiler/rustc_interface/src/passes.rs:823:5 - 71: {closure#0} - at ./compiler/rustc_query_impl/src/plumbing.rs:281:9 - [... omitted 22 frames ...] - 72: query_get_at>> - at ./compiler/rustc_middle/src/query/plumbing.rs:145:17 - 73: analysis - at ./compiler/rustc_middle/src/query/plumbing.rs:423:31 - 74: analysis - at ./compiler/rustc_middle/src/query/plumbing.rs:414:35 - 75: {closure#5} - at ./compiler/rustc_driver_impl/src/lib.rs:445:52 - 76: {closure#1}> - at ./compiler/rustc_middle/src/ty/context.rs:1296:37 - 77: {closure#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_middle/src/ty/context/tls.rs:82:9 - 78: try_with, rustc_middle::ty::context::tls::enter_context::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/std/src/thread/local.rs:283:12 - 79: with, rustc_middle::ty::context::tls::enter_context::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./library/std/src/thread/local.rs:260:9 - 80: enter_context>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_middle/src/ty/context/tls.rs:79:9 - 81: enter> - at ./compiler/rustc_middle/src/ty/context.rs:1296:9 - 82: {closure#1} - at ./compiler/rustc_driver_impl/src/lib.rs:445:13 - 83: enter, rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_interface/src/queries.rs:202:19 - 84: {closure#0} - at ./compiler/rustc_driver_impl/src/lib.rs:389:22 - 85: {closure#1}, rustc_driver_impl::run_compiler::{closure_env#0}> - at ./compiler/rustc_interface/src/interface.rs:502:27 - 86: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_interface/src/util.rs:154:13 - 87: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_interface/src/util.rs:106:21 - 88: set, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at /home/boxy/.cargo/registry/src/index.crates.io-6f17d22bba15001f/scoped-tls-1.0.1/src/lib.rs:137:9 - 89: create_session_globals_then, rustc_interface::util::run_in_thread_with_globals::{closure#0}::{closure#0}::{closure_env#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>>> - at ./compiler/rustc_span/src/lib.rs:134:5 - 90: {closure#0}, rustc_driver_impl::run_compiler::{closure_env#0}>, core::result::Result<(), rustc_span::ErrorGuaranteed>>, core::result::Result<(), rustc_span::ErrorGuaranteed>> - at ./compiler/rustc_interface/src/util.rs:105:17 -note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. - -error: the compiler unexpectedly panicked. this is a bug. - -note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md - -note: please make sure that you have updated to the latest nightly - -note: rustc 1.81.0-dev running on x86_64-unknown-linux-gnu - -note: compiler flags: -Z threads=1 -Z simulate-remapped-rust-src-base=/rustc/FAKE_PREFIX -Z translate-remapped-path-to-local-path=no -Z ignore-directory-in-diagnostics-source-blocks=/home/boxy/.cargo -Z ignore-directory-in-diagnostics-source-blocks=/media/Nyoomies/Repos/rust/vendor -C codegen-units=1 -Z ui-testing -Z deduplicate-diagnostics=no -Z write-long-types-to-disk=no -C strip=debuginfo -C prefer-dynamic -C rpath -C debuginfo=0 +error[E0367]: `Drop` impl requires `the constant `_` has type `usize`` but the struct it is implemented for does not + --> $DIR/unconstrained_const_param_on_drop.rs:3:6 + | +LL | impl Drop for Foo {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: the implementor must specify the same requirement + --> $DIR/unconstrained_const_param_on_drop.rs:1:1 + | +LL | struct Foo {} + | ^^^^^^^^^^ -query stack during panic: -#0 [adt_destructor] computing `Drop` impl for `Foo` -#1 [check_well_formed] checking that `Foo` is well-formed -#2 [check_mod_type_wf] checking that types are well-formed in top-level module -#3 [analysis] running analysis passes on this crate -end of query stack error[E0207]: the const parameter `UNUSED` is not constrained by the impl trait, self type, or predicates - --> $DIR/unconstrained_const_param_on_drop.rs:6:6 + --> $DIR/unconstrained_const_param_on_drop.rs:3:6 | LL | impl Drop for Foo {} | ^^^^^^^^^^^^^^^^^^^ unconstrained const parameter @@ -214,6 +19,7 @@ LL | impl Drop for Foo {} = note: expressions using a const parameter must map each value to a distinct output value = note: proving the result of expressions other than the parameter are unique is not supported -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0207`. +Some errors have detailed explanations: E0207, E0367. +For more information about an error, try `rustc --explain E0207`. From 8edf9b808427dfbe4425b49d213351f907f58b40 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 20 Mar 2024 20:30:04 -0400 Subject: [PATCH 03/10] Item bounds can reference self projections and still be object safe --- .../src/traits/object_safety.rs | 124 +++++++++++------- .../item-bounds-can-reference-self.rs | 11 ++ 2 files changed, 91 insertions(+), 44 deletions(-) create mode 100644 tests/ui/object-safety/item-bounds-can-reference-self.rs diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 1c6993bdd3729..29c18512e6d98 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -12,16 +12,16 @@ use super::elaborate; use crate::infer::TyCtxtInferExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{self, Obligation, ObligationCause}; +use crate::traits::{Obligation, ObligationCause}; use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::query::Providers; +use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{ self, EarlyBinder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; -use rustc_middle::ty::{GenericArg, GenericArgs}; use rustc_middle::ty::{TypeVisitableExt, Upcast}; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -195,7 +195,13 @@ fn predicates_reference_self( .predicates .iter() .map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, trait_ref), sp)) - .filter_map(|predicate| predicate_references_self(tcx, predicate)) + .filter_map(|(clause, sp)| { + // Super predicates cannot allow self projections, since they're + // impossible to make into existential bounds without eager resolution + // or something. + // e.g. `trait A: B`. + predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::No) + }) .collect() } @@ -204,20 +210,25 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .flat_map(|item| tcx.explicit_item_bounds(item.def_id).iter_identity_copied()) - .filter_map(|c| predicate_references_self(tcx, c)) + .filter_map(|(clause, sp)| { + // Item bounds *can* have self projections, since they never get + // their self type erased. + predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::Yes) + }) .collect() } fn predicate_references_self<'tcx>( tcx: TyCtxt<'tcx>, - (predicate, sp): (ty::Clause<'tcx>, Span), + trait_def_id: DefId, + predicate: ty::Clause<'tcx>, + sp: Span, + allow_self_projections: AllowSelfProjections, ) -> Option { - let self_ty = tcx.types.self_param; - let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into()); match predicate.kind().skip_binder() { ty::ClauseKind::Trait(ref data) => { // In the case of a trait predicate, we can skip the "self" type. - data.trait_ref.args[1..].iter().any(has_self_ty).then_some(sp) + data.trait_ref.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp) } ty::ClauseKind::Projection(ref data) => { // And similarly for projections. This should be redundant with @@ -235,9 +246,9 @@ fn predicate_references_self<'tcx>( // // This is ALT2 in issue #56288, see that for discussion of the // possible alternatives. - data.projection_term.args[1..].iter().any(has_self_ty).then_some(sp) + data.projection_term.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp) } - ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp), + ty::ClauseKind::ConstArgHasType(_ct, ty) => contains_illegal_self_type_reference(tcx, trait_def_id, ty, allow_self_projections).then_some(sp), ty::ClauseKind::WellFormed(..) | ty::ClauseKind::TypeOutlives(..) @@ -383,7 +394,12 @@ fn virtual_call_violations_for_method<'tcx>( let mut errors = Vec::new(); for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) { - if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) { + if contains_illegal_self_type_reference( + tcx, + trait_def_id, + sig.rebind(input_ty), + AllowSelfProjections::Yes, + ) { let span = if let Some(hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(sig, _), .. @@ -396,7 +412,12 @@ fn virtual_call_violations_for_method<'tcx>( errors.push(MethodViolationCode::ReferencesSelfInput(span)); } } - if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) { + if contains_illegal_self_type_reference( + tcx, + trait_def_id, + sig.output(), + AllowSelfProjections::Yes, + ) { errors.push(MethodViolationCode::ReferencesSelfOutput); } if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) { @@ -482,7 +503,7 @@ fn virtual_call_violations_for_method<'tcx>( return false; } - contains_illegal_self_type_reference(tcx, trait_def_id, pred) + contains_illegal_self_type_reference(tcx, trait_def_id, pred, AllowSelfProjections::Yes) }) { errors.push(MethodViolationCode::WhereClauseReferencesSelf); } @@ -711,10 +732,17 @@ fn receiver_is_dispatchable<'tcx>( infcx.predicate_must_hold_modulo_regions(&obligation) } +#[derive(Copy, Clone)] +enum AllowSelfProjections { + Yes, + No, +} + fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable>>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, value: T, + allow_self_projections: AllowSelfProjections, ) -> bool { // This is somewhat subtle. In general, we want to forbid // references to `Self` in the argument and return types, @@ -759,6 +787,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable>>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, supertraits: Option>, + allow_self_projections: AllowSelfProjections, } impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { @@ -780,39 +809,41 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable>>( ControlFlow::Continue(()) } ty::Alias(ty::Projection, ref data) => { - // This is a projected type `::X`. - - // Compute supertraits of current trait lazily. - if self.supertraits.is_none() { - let trait_ref = - ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id)); - self.supertraits = Some( - traits::supertraits(self.tcx, trait_ref).map(|t| t.def_id()).collect(), - ); - } + match self.allow_self_projections { + AllowSelfProjections::Yes => { + // This is a projected type `::X`. + + // Compute supertraits of current trait lazily. + if self.supertraits.is_none() { + self.supertraits = + Some(self.tcx.supertrait_def_ids(self.trait_def_id).collect()); + } - // Determine whether the trait reference `Foo as - // SomeTrait` is in fact a supertrait of the - // current trait. In that case, this type is - // legal, because the type `X` will be specified - // in the object type. Note that we can just use - // direct equality here because all of these types - // are part of the formal parameter listing, and - // hence there should be no inference variables. - let is_supertrait_of_current_trait = self - .supertraits - .as_ref() - .unwrap() - .contains(&data.trait_ref(self.tcx).def_id); - - // only walk contained types if it's not a super trait - if is_supertrait_of_current_trait { - ControlFlow::Continue(()) - } else { - t.super_visit_with(self) // POSSIBLY reporting an error + // Determine whether the trait reference `Foo as + // SomeTrait` is in fact a supertrait of the + // current trait. In that case, this type is + // legal, because the type `X` will be specified + // in the object type. Note that we can just use + // direct equality here because all of these types + // are part of the formal parameter listing, and + // hence there should be no inference variables. + let is_supertrait_of_current_trait = self + .supertraits + .as_ref() + .unwrap() + .contains(&data.trait_ref(self.tcx).def_id); + + // only walk contained types if it's not a super trait + if is_supertrait_of_current_trait { + ControlFlow::Continue(()) + } else { + t.super_visit_with(self) // POSSIBLY reporting an error + } + } + AllowSelfProjections::No => t.super_visit_with(self), } } - _ => t.super_visit_with(self), // walk contained types, if any + _ => t.super_visit_with(self), } } @@ -824,7 +855,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable>>( } value - .visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None }) + .visit_with(&mut IllegalSelfTypeVisitor { + tcx, + trait_def_id, + supertraits: None, + allow_self_projections, + }) .is_break() } diff --git a/tests/ui/object-safety/item-bounds-can-reference-self.rs b/tests/ui/object-safety/item-bounds-can-reference-self.rs new file mode 100644 index 0000000000000..4ae982e8f951f --- /dev/null +++ b/tests/ui/object-safety/item-bounds-can-reference-self.rs @@ -0,0 +1,11 @@ +//@ check-pass + +pub trait Foo { + type X: PartialEq; + type Y: PartialEq; + type Z: PartialEq; +} + +fn uwu(x: &dyn Foo) {} + +fn main() {} From 172cf9bef3411d479a0be98827647150cce48afd Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 6 Jun 2024 14:40:56 -0400 Subject: [PATCH 04/10] Fix unsoundness when associated types dont actually come from supertraits --- .../src/traits/object_safety.rs | 264 ++++++++++-------- .../almost-supertrait-associated-type.rs | 60 ++++ .../almost-supertrait-associated-type.stderr | 55 ++++ 3 files changed, 269 insertions(+), 110 deletions(-) create mode 100644 tests/ui/object-safety/almost-supertrait-associated-type.rs create mode 100644 tests/ui/object-safety/almost-supertrait-associated-type.stderr diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 29c18512e6d98..ec19cf2766816 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -12,17 +12,16 @@ use super::elaborate; use crate::infer::TyCtxtInferExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{Obligation, ObligationCause}; +use crate::traits::{util, Obligation, ObligationCause}; use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::query::Providers; -use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{ - self, EarlyBinder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitor, + self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt, + TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, Upcast, }; -use rustc_middle::ty::{TypeVisitableExt, Upcast}; use rustc_span::symbol::Symbol; use rustc_span::Span; use rustc_target::abi::Abi; @@ -738,130 +737,175 @@ enum AllowSelfProjections { No, } +/// This is somewhat subtle. In general, we want to forbid +/// references to `Self` in the argument and return types, +/// since the value of `Self` is erased. However, there is one +/// exception: it is ok to reference `Self` in order to access +/// an associated type of the current trait, since we retain +/// the value of those associated types in the object type +/// itself. +/// +/// ```rust,ignore (example) +/// trait SuperTrait { +/// type X; +/// } +/// +/// trait Trait : SuperTrait { +/// type Y; +/// fn foo(&self, x: Self) // bad +/// fn foo(&self) -> Self // bad +/// fn foo(&self) -> Option // bad +/// fn foo(&self) -> Self::Y // OK, desugars to next example +/// fn foo(&self) -> ::Y // OK +/// fn foo(&self) -> Self::X // OK, desugars to next example +/// fn foo(&self) -> ::X // OK +/// } +/// ``` +/// +/// However, it is not as simple as allowing `Self` in a projected +/// type, because there are illegal ways to use `Self` as well: +/// +/// ```rust,ignore (example) +/// trait Trait : SuperTrait { +/// ... +/// fn foo(&self) -> ::X; +/// } +/// ``` +/// +/// Here we will not have the type of `X` recorded in the +/// object type, and we cannot resolve `Self as SomeOtherTrait` +/// without knowing what `Self` is. fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable>>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, value: T, allow_self_projections: AllowSelfProjections, ) -> bool { - // This is somewhat subtle. In general, we want to forbid - // references to `Self` in the argument and return types, - // since the value of `Self` is erased. However, there is one - // exception: it is ok to reference `Self` in order to access - // an associated type of the current trait, since we retain - // the value of those associated types in the object type - // itself. - // - // ```rust - // trait SuperTrait { - // type X; - // } - // - // trait Trait : SuperTrait { - // type Y; - // fn foo(&self, x: Self) // bad - // fn foo(&self) -> Self // bad - // fn foo(&self) -> Option // bad - // fn foo(&self) -> Self::Y // OK, desugars to next example - // fn foo(&self) -> ::Y // OK - // fn foo(&self) -> Self::X // OK, desugars to next example - // fn foo(&self) -> ::X // OK - // } - // ``` - // - // However, it is not as simple as allowing `Self` in a projected - // type, because there are illegal ways to use `Self` as well: - // - // ```rust - // trait Trait : SuperTrait { - // ... - // fn foo(&self) -> ::X; - // } - // ``` - // - // Here we will not have the type of `X` recorded in the - // object type, and we cannot resolve `Self as SomeOtherTrait` - // without knowing what `Self` is. - - struct IllegalSelfTypeVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - supertraits: Option>, - allow_self_projections: AllowSelfProjections, - } + value + .visit_with(&mut IllegalSelfTypeVisitor { + tcx, + trait_def_id, + supertraits: None, + allow_self_projections, + }) + .is_break() +} - impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { - type Result = ControlFlow<()>; +struct IllegalSelfTypeVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + supertraits: Option>>, + allow_self_projections: AllowSelfProjections, +} - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { - match t.kind() { - ty::Param(_) => { - if t == self.tcx.types.self_param { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - } - ty::Alias(ty::Projection, ref data) - if self.tcx.is_impl_trait_in_trait(data.def_id) => - { - // We'll deny these later in their own pass +impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + match t.kind() { + ty::Param(_) => { + if t == self.tcx.types.self_param { + ControlFlow::Break(()) + } else { ControlFlow::Continue(()) } - ty::Alias(ty::Projection, ref data) => { - match self.allow_self_projections { - AllowSelfProjections::Yes => { - // This is a projected type `::X`. - - // Compute supertraits of current trait lazily. - if self.supertraits.is_none() { - self.supertraits = - Some(self.tcx.supertrait_def_ids(self.trait_def_id).collect()); - } + } + ty::Alias(ty::Projection, ref data) if self.tcx.is_impl_trait_in_trait(data.def_id) => { + // We'll deny these later in their own pass + ControlFlow::Continue(()) + } + ty::Alias(ty::Projection, ref data) => { + match self.allow_self_projections { + AllowSelfProjections::Yes => { + // This is a projected type `::X`. + + // Compute supertraits of current trait lazily. + if self.supertraits.is_none() { + self.supertraits = Some( + util::supertraits( + self.tcx, + ty::Binder::dummy(ty::TraitRef::identity( + self.tcx, + self.trait_def_id, + )), + ) + .map(|trait_ref| { + self.tcx.erase_regions( + self.tcx.instantiate_bound_regions_with_erased(trait_ref), + ) + }) + .collect(), + ); + } - // Determine whether the trait reference `Foo as - // SomeTrait` is in fact a supertrait of the - // current trait. In that case, this type is - // legal, because the type `X` will be specified - // in the object type. Note that we can just use - // direct equality here because all of these types - // are part of the formal parameter listing, and - // hence there should be no inference variables. - let is_supertrait_of_current_trait = self - .supertraits - .as_ref() - .unwrap() - .contains(&data.trait_ref(self.tcx).def_id); - - // only walk contained types if it's not a super trait - if is_supertrait_of_current_trait { - ControlFlow::Continue(()) - } else { - t.super_visit_with(self) // POSSIBLY reporting an error - } + // Determine whether the trait reference `Foo as + // SomeTrait` is in fact a supertrait of the + // current trait. In that case, this type is + // legal, because the type `X` will be specified + // in the object type. Note that we can just use + // direct equality here because all of these types + // are part of the formal parameter listing, and + // hence there should be no inference variables. + let is_supertrait_of_current_trait = + self.supertraits.as_ref().unwrap().contains( + &data.trait_ref(self.tcx).fold_with( + &mut EraseEscapingBoundRegions { + tcx: self.tcx, + binder: ty::INNERMOST, + }, + ), + ); + + // only walk contained types if it's not a super trait + if is_supertrait_of_current_trait { + ControlFlow::Continue(()) + } else { + t.super_visit_with(self) // POSSIBLY reporting an error } - AllowSelfProjections::No => t.super_visit_with(self), } + AllowSelfProjections::No => t.super_visit_with(self), } - _ => t.super_visit_with(self), } + _ => t.super_visit_with(self), } + } - fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { - // Constants can only influence object safety if they are generic and reference `Self`. - // This is only possible for unevaluated constants, so we walk these here. - self.tcx.expand_abstract_consts(ct).super_visit_with(self) - } + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { + // Constants can only influence object safety if they are generic and reference `Self`. + // This is only possible for unevaluated constants, so we walk these here. + self.tcx.expand_abstract_consts(ct).super_visit_with(self) } +} - value - .visit_with(&mut IllegalSelfTypeVisitor { - tcx, - trait_def_id, - supertraits: None, - allow_self_projections, - }) - .is_break() +struct EraseEscapingBoundRegions<'tcx> { + tcx: TyCtxt<'tcx>, + binder: ty::DebruijnIndex, +} + +impl<'tcx> TypeFolder> for EraseEscapingBoundRegions<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> + where + T: TypeFoldable>, + { + self.binder.shift_in(1); + let result = t.super_fold_with(self); + self.binder.shift_out(1); + result + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + if let ty::ReBound(debruijn, _) = *r + && debruijn < self.binder + { + r + } else { + self.tcx.lifetimes.re_erased + } + } } pub fn contains_illegal_impl_trait_in_trait<'tcx>( diff --git a/tests/ui/object-safety/almost-supertrait-associated-type.rs b/tests/ui/object-safety/almost-supertrait-associated-type.rs new file mode 100644 index 0000000000000..963cdff526ee6 --- /dev/null +++ b/tests/ui/object-safety/almost-supertrait-associated-type.rs @@ -0,0 +1,60 @@ +// Test for fixed unsoundness in #126079. +// Enforces that the associated types that are object safe + +use std::marker::PhantomData; + +fn transmute(t: T) -> U { + (&PhantomData:: as &dyn Foo).transmute(t) + //~^ ERROR the trait `Foo` cannot be made into an object + //~| ERROR the trait `Foo` cannot be made into an object +} + +struct ActuallySuper; +struct NotActuallySuper; +trait Super { + type Assoc; +} + +trait Dyn { + type Out; +} +impl Dyn for dyn Foo + '_ { +//~^ ERROR the trait `Foo` cannot be made into an object + type Out = U; +} +impl + ?Sized, U> Super for S { + type Assoc = U; +} + +trait Foo: Super +where + ::Assoc: Super +{ + fn transmute(&self, t: T) -> >::Assoc; +} + +trait Mirror { + type Assoc: ?Sized; +} +impl Mirror for T { + type Assoc = T; +} + +impl Foo for PhantomData { + fn transmute(&self, t: T) -> T { + t + } +} +impl Super for PhantomData { + type Assoc = T; +} +impl Super for PhantomData { + type Assoc = T; +} + +fn main() { + let x = String::from("hello, world"); + let s = transmute::<&str, &'static str>(x.as_str()); + drop(x); + println!("> {s}"); +} diff --git a/tests/ui/object-safety/almost-supertrait-associated-type.stderr b/tests/ui/object-safety/almost-supertrait-associated-type.stderr new file mode 100644 index 0000000000000..97a51c2f38164 --- /dev/null +++ b/tests/ui/object-safety/almost-supertrait-associated-type.stderr @@ -0,0 +1,55 @@ +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/almost-supertrait-associated-type.rs:21:20 + | +LL | impl Dyn for dyn Foo + '_ { + | ^^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/almost-supertrait-associated-type.rs:33:34 + | +LL | trait Foo: Super + | --- this trait cannot be made into an object... +... +LL | fn transmute(&self, t: T) -> >::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `transmute` references the `Self` type in its return type + = help: consider moving `transmute` to another trait + = help: only type `std::marker::PhantomData` implements the trait, consider using it directly instead + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/almost-supertrait-associated-type.rs:7:27 + | +LL | (&PhantomData:: as &dyn Foo).transmute(t) + | ^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/almost-supertrait-associated-type.rs:33:34 + | +LL | trait Foo: Super + | --- this trait cannot be made into an object... +... +LL | fn transmute(&self, t: T) -> >::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `transmute` references the `Self` type in its return type + = help: consider moving `transmute` to another trait + = help: only type `std::marker::PhantomData` implements the trait, consider using it directly instead + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/almost-supertrait-associated-type.rs:7:6 + | +LL | (&PhantomData:: as &dyn Foo).transmute(t) + | ^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/almost-supertrait-associated-type.rs:33:34 + | +LL | trait Foo: Super + | --- this trait cannot be made into an object... +... +LL | fn transmute(&self, t: T) -> >::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `transmute` references the `Self` type in its return type + = help: consider moving `transmute` to another trait + = help: only type `std::marker::PhantomData` implements the trait, consider using it directly instead + = note: required for the cast from `&PhantomData` to `&dyn Foo` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0038`. From c5dadd0408bc5654f001efe0f2224ff67e5e90a7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 19 Jul 2024 11:02:59 +1000 Subject: [PATCH 05/10] Use `#[rustfmt::skip]` on some `use` groups to prevent reordering. `use` declarations will be reformatted in #125443. Very rarely, there is a desire to force a group of `use` declarations together in a way that auto-formatting will break up. E.g. when you want a single comment to apply to a group. #126776 dealt with all of these in the codebase, ensuring that no comments intended for multiple `use` declarations would end up in the wrong place. But some people were unhappy with it. This commit uses `#[rustfmt::skip]` to create these custom `use` groups in an idiomatic way for a few of the cases changed in #126776. This works because rustfmt treats any `use` item annotated with `#[rustfmt::skip]` as a barrier and won't reorder other `use` items around it. --- compiler/rustc_const_eval/src/interpret/validity.rs | 2 ++ compiler/rustc_lint/src/lib.rs | 1 + library/core/src/char/mod.rs | 5 +++++ library/core/src/unicode/mod.rs | 10 ++++++---- library/std/src/rt.rs | 2 ++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 4da7e233889f0..3dc3e6c883320 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -34,6 +34,8 @@ use super::{ Pointer, Projectable, Scalar, ValueVisitor, }; +// for the validation errors +#[rustfmt::skip] use super::InterpError::UndefinedBehavior as Ub; use super::InterpError::Unsupported as Unsup; use super::UndefinedBehaviorInfo::*; diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 290f91045c455..1ed0b2c0d6941 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -120,6 +120,7 @@ use types::*; use unit_bindings::*; use unused::*; +#[rustfmt::skip] pub use builtin::{MissingDoc, SoftLints}; pub use context::{CheckLintNameResult, FindLintError, LintStore}; pub use context::{EarlyContext, LateContext, LintContext}; diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 3c641a2e01c93..37c27ecb8c4d6 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -24,6 +24,8 @@ mod convert; mod decode; mod methods; +// stable re-exports +#[rustfmt::skip] #[stable(feature = "try_from", since = "1.34.0")] pub use self::convert::CharTryFromError; #[stable(feature = "char_from_str", since = "1.20.0")] @@ -31,11 +33,14 @@ pub use self::convert::ParseCharError; #[stable(feature = "decode_utf16", since = "1.9.0")] pub use self::decode::{DecodeUtf16, DecodeUtf16Error}; +// perma-unstable re-exports +#[rustfmt::skip] #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] pub use self::methods::encode_utf16_raw; // perma-unstable #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] pub use self::methods::encode_utf8_raw; // perma-unstable +#[rustfmt::skip] use crate::ascii; use crate::error::Error; use crate::escape; diff --git a/library/core/src/unicode/mod.rs b/library/core/src/unicode/mod.rs index 5ddd9f7476dd8..6066aa9921607 100644 --- a/library/core/src/unicode/mod.rs +++ b/library/core/src/unicode/mod.rs @@ -1,13 +1,15 @@ #![unstable(feature = "unicode_internals", issue = "none")] #![allow(missing_docs)] -// The `pub use` ones are for use in alloc, and are not re-exported in std. - -pub(crate) use unicode_data::alphabetic::lookup as Alphabetic; +// for use in alloc, not re-exported in std. +#[rustfmt::skip] pub use unicode_data::case_ignorable::lookup as Case_Ignorable; pub use unicode_data::cased::lookup as Cased; -pub(crate) use unicode_data::cc::lookup as Cc; pub use unicode_data::conversions; + +#[rustfmt::skip] +pub(crate) use unicode_data::alphabetic::lookup as Alphabetic; +pub(crate) use unicode_data::cc::lookup as Cc; pub(crate) use unicode_data::grapheme_extend::lookup as Grapheme_Extend; pub(crate) use unicode_data::lowercase::lookup as Lowercase; pub(crate) use unicode_data::n::lookup as N; diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index deb4a8fa7eed0..307a543c9d215 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -16,9 +16,11 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(unused_macros)] +#[rustfmt::skip] pub use crate::panicking::{begin_panic, panic_count}; pub use core::panicking::{panic_display, panic_fmt}; +#[rustfmt::skip] use crate::sync::Once; use crate::sys; use crate::thread::{self, Thread}; From 118a70f38c8f4898329d701a17d131a5b7bd7b48 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 22 Jul 2024 20:36:43 +1000 Subject: [PATCH 06/10] Various notes on match lowering --- .../rustc_mir_build/src/build/matches/mod.rs | 236 ++++++++++++++---- .../rustc_mir_build/src/build/matches/test.rs | 16 +- 2 files changed, 198 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 95bc8b3d0cbc4..bd9a385790589 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -528,12 +528,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { end_block.unit() } - /// Binds the variables and ascribes types for a given `match` arm or - /// `let` binding. + /// For a top-level `match` arm or a `let` binding, binds the variables and + /// ascribes types, and also checks the match arm guard (if present). /// - /// Also check if the guard matches, if it's provided. /// `arm_scope` should be `Some` if and only if this is called for a /// `match` arm. + /// + /// In the presence of or-patterns, a match arm might have multiple + /// sub-branches representing different ways to match, with each sub-branch + /// requiring its own bindings and its own copy of the guard. This method + /// handles those sub-branches individually, and then has them jump together + /// to a common block. + /// + /// Returns a single block that the match arm can be lowered into. + /// (For `let` bindings, this is the code that can use the bindings.) fn bind_pattern( &mut self, outer_source_info: SourceInfo, @@ -1022,7 +1030,8 @@ impl<'tcx> PatternExtraData<'tcx> { } } -/// A pattern in a form suitable for generating code. +/// A pattern in a form suitable for lowering the match tree, with all irrefutable +/// patterns simplified away, and or-patterns sorted to the end. /// /// Here, "flat" indicates that the pattern's match pairs have been recursively /// simplified by [`Builder::simplify_match_pairs`]. They are not necessarily @@ -1055,36 +1064,89 @@ impl<'tcx, 'pat> FlatPat<'pat, 'tcx> { ascriptions: Vec::new(), is_never: pattern.is_never_pattern(), }; - // Partly-flatten and sort the match pairs, while recording extra data. + // Recursively remove irrefutable match pairs, while recording their + // bindings/ascriptions, and sort or-patterns after other match pairs. cx.simplify_match_pairs(&mut match_pairs, &mut extra_data); Self { match_pairs, extra_data } } } +/// Candidates are a generalization of (a) top-level match arms, and +/// (b) sub-branches of or-patterns, allowing the match-lowering process to handle +/// them both in a mostly-uniform way. For example, the list of candidates passed +/// to [`Builder::match_candidates`] will often contain a mixture of top-level +/// candidates and or-pattern subcandidates. +/// +/// At the start of match lowering, there is one candidate for each match arm. +/// During match lowering, arms with or-patterns will be expanded into a tree +/// of candidates, where each "leaf" candidate represents one of the ways for +/// the arm pattern to successfully match. #[derive(Debug)] struct Candidate<'pat, 'tcx> { /// For the candidate to match, all of these must be satisfied... - // Invariant: all the match pairs are recursively simplified. - // Invariant: or-patterns must be sorted at the end. + /// + /// --- + /// Initially contains a list of match pairs created by [`FlatPat`], but is + /// subsequently mutated (in a queue-like way) while lowering the match tree. + /// When this list becomes empty, the candidate is fully matched and becomes + /// a leaf (see [`Builder::select_matched_candidate`]). + /// + /// Key mutations include: + /// + /// - When a match pair is fully satisfied by a test, it is removed from the + /// list, and its subpairs are added instead (see [`Builder::sort_candidate`]). + /// - During or-pattern expansion, any leading or-pattern is removed, and is + /// converted into subcandidates (see [`Builder::expand_and_match_or_candidates`]). + /// - After a candidate's subcandidates have been lowered, a copy of any remaining + /// or-patterns is added to each leaf subcandidate + /// (see [`Builder::test_remaining_match_pairs_after_or`]). + /// + /// Invariants: + /// - All [`TestCase::Irrefutable`] patterns have been removed by simplification. + /// - All or-patterns ([`TestCase::Or`]) have been sorted to the end. match_pairs: Vec>, /// ...and if this is non-empty, one of these subcandidates also has to match... - // Invariant: at the end of the algorithm, this must never contain a `is_never` candidate - // because that would break binding consistency. + /// + /// --- + /// Initially a candidate has no subcandidates; they are added (and then immediately + /// lowered) during or-pattern expansion. Their main function is to serve as _output_ + /// of match tree lowering, allowing later steps to see the leaf candidates that + /// represent a match of the entire match arm. + /// + /// A candidate no subcandidates is either incomplete (if it has match pairs left), + /// or is a leaf in the match tree. A candidate with one or more subcandidates is + /// an internal node in the match tree. + /// + /// Invariant: at the end of match tree lowering, this must not contain an + /// `is_never` candidate, because that would break binding consistency. + /// - See [`Builder::remove_never_subcandidates`]. subcandidates: Vec>, /// ...and if there is a guard it must be evaluated; if it's `false` then branch to `otherwise_block`. + /// + /// --- + /// For subcandidates, this is copied from the parent candidate, so it indicates + /// whether the enclosing match arm has a guard. has_guard: bool, - /// If the candidate matches, bindings and ascriptions must be established. + /// Holds extra pattern data that was prepared by [`FlatPat`], including bindings and + /// ascriptions that must be established if this candidate succeeds. extra_data: PatternExtraData<'tcx>, - /// If we filled `self.subcandidate`, we store here the span of the or-pattern they came from. - // Invariant: it is `None` iff `subcandidates.is_empty()`. + /// When setting `self.subcandidates`, we store here the span of the or-pattern they came from. + /// + /// --- + /// Invariant: it is `None` iff `subcandidates.is_empty()`. + /// - FIXME: We sometimes don't unset this when clearing `subcandidates`. or_span: Option, /// The block before the `bindings` have been established. + /// + /// After the match tree has been lowered, [`Builder::lower_match_arms`] + /// will use this as the start point for lowering bindings and guards, and + /// then jump to a shared block containing the arm body. pre_binding_block: Option, /// The block to branch to if the guard or a nested candidate fails to match. @@ -1144,14 +1206,24 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { /// A depth-first traversal of the `Candidate` and all of its recursive /// subcandidates. +/// +/// This signature is very generic, to support traversing candidate trees by +/// reference or by value, and to allow a mutable "context" to be shared by the +/// traversal callbacks. Most traversals can use the simpler +/// [`Candidate::visit_leaves`] wrapper instead. fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>( candidate: C, context: &mut T, + // Called when visiting a "leaf" candidate (with no subcandidates). visit_leaf: &mut impl FnMut(C, &mut T), + // Called when visiting a "node" candidate (with one or more subcandidates). + // Returns an iterator over the candidate's children (by value or reference). + // Can perform setup before visiting the node's children. get_children: impl Copy + Fn(C, &mut T) -> I, + // Called after visiting a "node" candidate's children. complete_children: impl Copy + Fn(&mut T), ) where - C: Borrow>, + C: Borrow>, // Typically `Candidate` or `&mut Candidate` I: Iterator, { if candidate.borrow().subcandidates.is_empty() { @@ -1182,6 +1254,24 @@ struct Ascription<'tcx> { variance: ty::Variance, } +/// Partial summary of a [`thir::Pat`], indicating what sort of test should be +/// performed to match/reject the pattern, and what the desired test outcome is. +/// This avoids having to perform a full match on [`thir::PatKind`] in some places, +/// and helps [`TestKind::Switch`] and [`TestKind::SwitchInt`] know what target +/// values to use. +/// +/// Created by [`MatchPairTree::for_pattern`], and then inspected primarily by: +/// - [`Builder::pick_test_for_match_pair`] (to choose a test) +/// - [`Builder::sort_candidate`] (to see how the test interacts with a match pair) +/// +/// Two variants are unlike the others and deserve special mention: +/// +/// - [`Self::Irrefutable`] is only used temporarily when building a [`MatchPairTree`]. +/// They are then flattened away by [`Builder::simplify_match_pairs`], with any +/// bindings/ascriptions incorporated into the enclosing [`FlatPat`]. +/// - [`Self::Or`] are not tested directly like the other variants. Instead they +/// participate in or-pattern expansion, where they are transformed into subcandidates. +/// - See [`Builder::expand_and_match_or_candidates`]. #[derive(Debug, Clone)] enum TestCase<'pat, 'tcx> { Irrefutable { binding: Option>, ascription: Option> }, @@ -1224,6 +1314,12 @@ pub(crate) struct MatchPairTree<'pat, 'tcx> { test_case: TestCase<'pat, 'tcx>, /// ... and these subpairs must match. + /// + /// --- + /// Subpairs typically represent tests that can only be performed after their + /// parent has succeeded. For example, the pattern `Some(3)` might have an + /// outer match pair that tests for the variant `Some`, and then a subpair + /// that tests its field for the value `3`. subpairs: Vec, /// The pattern this was created from. @@ -1234,15 +1330,22 @@ pub(crate) struct MatchPairTree<'pat, 'tcx> { #[derive(Clone, Debug, PartialEq)] enum TestKind<'tcx> { /// Test what enum variant a value is. + /// + /// The subset of expected variants is not stored here; instead they are + /// extracted from the [`TestCase`]s of the candidates participating in the + /// test. Switch { /// The enum type being tested. adt_def: ty::AdtDef<'tcx>, }, /// Test what value an integer or `char` has. + /// + /// The test's target values are not stored here; instead they are extracted + /// from the [`TestCase`]s of the candidates participating in the test. SwitchInt, - /// Test what value a `bool` has. + /// Test whether a `bool` is `true` or `false`. If, /// Test for equality with value, possibly after an unsizing coercion to @@ -1258,7 +1361,7 @@ enum TestKind<'tcx> { /// Test whether the value falls within an inclusive or exclusive range. Range(Box>), - /// Test that the length of the slice is equal to `len`. + /// Test that the length of the slice is `== len` or `>= len`. Len { len: u64, op: BinOp }, /// Call `Deref::deref[_mut]` on the value. @@ -1385,20 +1488,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// The main match algorithm. It begins with a set of candidates `candidates` and has the job of /// generating code that branches to an appropriate block if the scrutinee matches one of these /// candidates. The - /// candidates are sorted such that the first item in the list + /// candidates are ordered such that the first item in the list /// has the highest priority. When a candidate is found to match /// the value, we will set and generate a branch to the appropriate /// pre-binding block. /// /// If none of the candidates apply, we continue to the returned `otherwise_block`. /// - /// It might be surprising that the input can be non-exhaustive. - /// Indeed, for matches, initially, it is not, because all matches are - /// exhaustive in Rust. But during processing we sometimes divide - /// up the list of candidates and recurse with a non-exhaustive - /// list. This is how our lowering approach (called "backtracking - /// automaton" in the literature) works. - /// See [`Builder::test_candidates`] for more details. + /// Note that while `match` expressions in the Rust language are exhaustive, + /// candidate lists passed to this method are often _non-exhaustive_. + /// For example, the match lowering process will frequently divide up the + /// list of candidates, and recursively call this method with a non-exhaustive + /// subset of candidates. + /// See [`Builder::test_candidates`] for more details on this + /// "backtracking automata" approach. /// /// For an example of how we use `otherwise_block`, consider: /// ``` @@ -1478,14 +1581,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return start_block; } [first, remaining @ ..] if first.match_pairs.is_empty() => { - // The first candidate has satisfied all its match pairs; we link it up and continue - // with the remaining candidates. + // The first candidate has satisfied all its match pairs. + // We record the blocks that will be needed by match arm lowering, + // and then continue with the remaining candidates. let remainder_start = self.select_matched_candidate(first, start_block); remainder_start.and(remaining) } candidates if candidates.iter().any(|candidate| candidate.starts_with_or_pattern()) => { - // If any candidate starts with an or-pattern, we have to expand the or-pattern before we - // can proceed further. + // If any candidate starts with an or-pattern, we want to expand or-patterns + // before we do any more tests. + // + // The only candidate we strictly _need_ to expand here is the first one. + // But by expanding other candidates as early as possible, we unlock more + // opportunities to include them in test outcomes, making the match tree + // smaller and simpler. self.expand_and_match_or_candidates(span, scrutinee_span, start_block, candidates) } candidates => { @@ -1588,6 +1697,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until); // Expand one level of or-patterns for each candidate in `candidates_to_expand`. + // We take care to preserve the relative ordering of candidates, so that + // or-patterns are expanded in their parent's relative position. let mut expanded_candidates = Vec::new(); for candidate in candidates_to_expand.iter_mut() { if candidate.starts_with_or_pattern() { @@ -1608,7 +1719,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - // Process the expanded candidates. + // Recursively lower the part of the match tree represented by the + // expanded candidates. This is where subcandidates actually get lowered! let remainder_start = self.match_candidates( span, scrutinee_span, @@ -1628,6 +1740,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.remove_never_subcandidates(candidate); } } + // It's important to perform the above simplifications _before_ dealing + // with remaining match pairs, to avoid exponential blowup if possible + // (for trivial or-patterns), and avoid useless work (for never patterns). if let Some(last_candidate) = candidates_to_expand.last_mut() { self.test_remaining_match_pairs_after_or(span, scrutinee_span, last_candidate); } @@ -1808,6 +1923,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .all(|match_pair| matches!(match_pair.test_case, TestCase::Or { .. })) ); + // Visit each leaf candidate within this subtree, add a copy of the remaining + // match pairs to it, and then recursively lower the rest of the match tree + // from that point. candidate.visit_leaves(|leaf_candidate| { // At this point the leaf's own match pairs have all been lowered // and removed, so `extend` and assignment are equivalent, @@ -1860,17 +1978,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (match_place, test) } - /// Given a test, we sort the input candidates into several buckets. If a candidate only matches - /// in one of the branches of `test`, we move it there. If it could match in more than one of - /// the branches of `test`, we stop sorting candidates. + /// Given a test, we partition the input candidates into several buckets. + /// If a candidate matches in exactly one of the branches of `test` + /// (and no other branches), we put it into the corresponding bucket. + /// If it could match in more than one of the branches of `test`, the test + /// doesn't usefully apply to it, and we stop partitioning candidates. + /// + /// Importantly, we also **mutate** the branched candidates to remove match pairs + /// that are entailed by the outcome of the test, and add any sub-pairs of the + /// removed pairs. /// /// This returns a pair of /// - the candidates that weren't sorted; /// - for each possible outcome of the test, the candidates that match in that outcome. /// - /// Moreover, we transform the branched candidates to reflect the fact that we know which - /// outcome of `test` occurred. - /// /// For example: /// ``` /// # let (x, y, z) = (true, true, true); @@ -1883,14 +2004,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// # ; /// ``` /// - /// Assume we are testing on `x`. There are 2 overlapping candidate sets: - /// - If the outcome is that `x` is true, candidates 0, 2, and 3 - /// - If the outcome is that `x` is false, candidates 1 and 2 + /// Assume we are testing on `x`. Conceptually, there are 2 overlapping candidate sets: + /// - If the outcome is that `x` is true, candidates {0, 2, 3} are possible + /// - If the outcome is that `x` is false, candidates {1, 2} are possible /// - /// Following our algorithm, candidate 0 is sorted into outcome `x == true`, candidate 1 goes - /// into outcome `x == false`, and candidate 2 and 3 remain unsorted. + /// Following our algorithm: + /// - Candidate 0 is sorted into outcome `x == true` + /// - Candidate 1 is sorted into outcome `x == false` + /// - Candidate 2 remains unsorted, because testing `x` has no effect on it + /// - Candidate 3 remains unsorted, because a previous candidate (2) was unsorted + /// - This helps preserve the illusion that candidates are tested "in order" /// - /// The sorted candidates are transformed: + /// The sorted candidates are mutated to remove entailed match pairs: /// - candidate 0 becomes `[z @ true]` since we know that `x` was `true`; /// - candidate 1 becomes `[y @ false]` since we know that `x` was `false`. fn sort_candidates<'b, 'c, 'pat>( @@ -1933,15 +2058,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (candidates, target_candidates) } - /// This is the most subtle part of the match lowering algorithm. At this point, the input - /// candidates have been fully simplified, so all remaining match-pairs require some sort of - /// test. + /// This is the most subtle part of the match lowering algorithm. At this point, there are + /// no fully-satisfied candidates, and no or-patterns to expand, so we actually need to + /// perform some sort of test to make progress. /// /// Once we pick what sort of test we are going to perform, this test will help us winnow down /// our candidates. So we walk over the candidates (from high to low priority) and check. We - /// compute, for each outcome of the test, a transformed list of candidates. If a candidate - /// matches in a single branch of our test, we add it to the corresponding outcome. We also - /// transform it to record the fact that we know which outcome occurred. + /// compute, for each outcome of the test, a list of (modified) candidates. If a candidate + /// matches in exactly one branch of our test, we add it to the corresponding outcome. We also + /// **mutate its list of match pairs** if appropriate, to reflect the fact that we know which + /// outcome occurred. /// /// For example, if we are testing `x.0`'s variant, and we have a candidate `(x.0 @ Some(v), x.1 /// @ 22)`, then we would have a resulting candidate of `((x.0 as Some).0 @ v, x.1 @ 22)` in the @@ -2036,32 +2162,38 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], start_block: BasicBlock, ) -> BlockAnd<&'b mut [&'c mut Candidate<'pat, 'tcx>]> { - // Extract the match-pair from the highest priority candidate and build a test from it. + // Choose a match pair from the first candidate, and use it to determine a + // test to perform that will confirm or refute that match pair. let (match_place, test) = self.pick_test(candidates); // For each of the N possible test outcomes, build the vector of candidates that applies if - // the test has that particular outcome. + // the test has that particular outcome. This also mutates the candidates to remove match + // pairs that are fully satisfied by the relevant outcome. let (remaining_candidates, target_candidates) = self.sort_candidates(match_place, &test, candidates); - // The block that we should branch to if none of the - // `target_candidates` match. + // The block that we should branch to if none of the `target_candidates` match. let remainder_start = self.cfg.start_new_block(); - // For each outcome of test, process the candidates that still apply. + // For each outcome of the test, recursively lower the rest of the match tree + // from that point. (Note that we haven't lowered the actual test yet!) let target_blocks: FxIndexMap<_, _> = target_candidates .into_iter() .map(|(branch, mut candidates)| { let branch_start = self.cfg.start_new_block(); + // Recursively lower the rest of the match tree after the relevant outcome. let branch_otherwise = self.match_candidates(span, scrutinee_span, branch_start, &mut *candidates); + + // Link up the `otherwise` block of the subtree to `remainder_start`. let source_info = self.source_info(span); self.cfg.goto(branch_otherwise, source_info, remainder_start); (branch, branch_start) }) .collect(); - // Perform the test, branching to one of N blocks. + // Perform the chosen test, branching to one of the N subtrees prepared above + // (or to `remainder_start` if no outcome was satisfied). self.perform_test( span, scrutinee_span, diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 8a02ea1a06de4..802193b8ddfde 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -51,6 +51,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestCase::Never => TestKind::Never, + // Or-patterns are not tested directly; instead they are expanded into subcandidates, + // which are then distinguished by testing whatever non-or patterns they contain. TestCase::Or { .. } => bug!("or-patterns should have already been handled"), TestCase::Irrefutable { .. } => span_bug!( @@ -544,6 +546,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .enumerate() .find(|&(_, mp)| mp.place == Some(test_place))?; + // If true, the match pair is completely entailed by its corresponding test + // branch, so it can be removed. If false, the match pair is _compatible_ + // with its test branch, but still needs a more specific test. let fully_matched; let ret = match (&test.kind, &match_pair.test_case) { // If we are performing a variant switch, then this @@ -565,8 +570,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (TestKind::SwitchInt, &TestCase::Constant { value }) if is_switch_ty(match_pair.pattern.ty) => { - // Beware: there might be some ranges sorted into the failure case; we must not add - // a success case that could be matched by one of these ranges. + // An important invariant of candidate sorting is that a candidate + // must not match in multiple branches. For `SwitchInt` tests, adding + // a new value might invalidate that property for range patterns that + // have already been sorted into the failure arm, so we must take care + // not to add such values here. let is_covering_range = |test_case: &TestCase<'_, 'tcx>| { test_case.as_range().is_some_and(|range| { matches!(range.contains(value, self.tcx, self.param_env), None | Some(true)) @@ -591,6 +599,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } (TestKind::SwitchInt, TestCase::Range(range)) => { + // When performing a `SwitchInt` test, a range pattern can be + // sorted into the failure arm if it doesn't contain _any_ of + // the values being tested. (This restricts what values can be + // added to the test by subsequent candidates.) fully_matched = false; let not_contained = sorted_candidates.keys().filter_map(|br| br.as_constant()).copied().all( From 31f31aa4714ff677befa50d95fb60cc47681b638 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 25 Jul 2024 16:41:51 +1000 Subject: [PATCH 07/10] Remove an obsolete comment The test mentioned by this comment was deleted long ago by . --- compiler/rustc_mir_build/src/build/matches/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index bd9a385790589..6ac8f0d002346 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -646,12 +646,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Optimize the case of `let x: T = ...` to write directly // into `x` and then require that `T == typeof(x)`. - // - // Weirdly, this is needed to prevent the - // `intrinsic-move-val.rs` test case from crashing. That - // test works with uninitialized values in a rather - // dubious way, so it may be that the test is kind of - // broken. PatKind::AscribeUserType { subpattern: box Pat { From 38931cd227a9e177ddb749da26ce03f2eb6532aa Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Thu, 25 Jul 2024 16:47:30 +0000 Subject: [PATCH 08/10] LLVM: LLVM-20.0 removes MMX types See llvm/llvm-project#98505 --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 2 -- compiler/rustc_codegen_ssa/src/common.rs | 1 - compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 2 -- 3 files changed, 5 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index ae46200d3f554..3beda28ac1fd4 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -305,7 +305,6 @@ pub enum TypeKind { Pointer = 12, Vector = 13, Metadata = 14, - X86_MMX = 15, Token = 16, ScalableVector = 17, BFloat = 18, @@ -330,7 +329,6 @@ impl TypeKind { TypeKind::Pointer => rustc_codegen_ssa::common::TypeKind::Pointer, TypeKind::Vector => rustc_codegen_ssa::common::TypeKind::Vector, TypeKind::Metadata => rustc_codegen_ssa::common::TypeKind::Metadata, - TypeKind::X86_MMX => rustc_codegen_ssa::common::TypeKind::X86_MMX, TypeKind::Token => rustc_codegen_ssa::common::TypeKind::Token, TypeKind::ScalableVector => rustc_codegen_ssa::common::TypeKind::ScalableVector, TypeKind::BFloat => rustc_codegen_ssa::common::TypeKind::BFloat, diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 27b0f127e9262..ea2fd482e1fc2 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -91,7 +91,6 @@ pub enum TypeKind { Pointer, Vector, Metadata, - X86_MMX, Token, ScalableVector, BFloat, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 14757b27a3758..4cdd8af1008c0 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1419,8 +1419,6 @@ extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) { return LLVMPointerTypeKind; case Type::FixedVectorTyID: return LLVMVectorTypeKind; - case Type::X86_MMXTyID: - return LLVMX86_MMXTypeKind; case Type::TokenTyID: return LLVMTokenTypeKind; case Type::ScalableVectorTyID: From 8174f9b44b91fbcddfb1dfe667195e21a305fea9 Mon Sep 17 00:00:00 2001 From: Boxy Date: Thu, 25 Jul 2024 19:47:12 +0100 Subject: [PATCH 09/10] Stop using `unsized_const_parameters` in core/std --- library/core/src/intrinsics/simd.rs | 12 ------------ library/core/src/lib.rs | 1 - library/core/src/marker.rs | 2 ++ .../miri/tests/pass/intrinsics/portable-simd.rs | 15 ++++++++++++++- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index 30734c020b39b..0c21e6f31a827 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -243,18 +243,6 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_shuffle(x: T, y: T, idx: U) -> V; - /// Shuffle two vectors by const indices. - /// - /// `T` must be a vector. - /// - /// `U` must be a vector with the same element type as `T` and the same length as `IDX`. - /// - /// Returns a new vector such that element `i` is selected from `xy[IDX[i]]`, where `xy` - /// is the concatenation of `x` and `y`. It is a compile-time error if `IDX[i]` is out-of-bounds - /// of `xy`. - #[rustc_nounwind] - pub fn simd_shuffle_generic(x: T, y: T) -> U; - /// Read a vector of pointers. /// /// `T` must be a vector. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0ec46412e9522..02cb02dce1d87 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -249,7 +249,6 @@ #![feature(transparent_unions)] #![feature(try_blocks)] #![feature(unboxed_closures)] -#![feature(unsized_const_params)] #![feature(unsized_fn_params)] #![feature(with_negative_coherence)] // tidy-alphabetical-end diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index a87528033c03b..cf428e36ea7a0 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -42,6 +42,8 @@ use crate::hash::Hasher; /// } /// ``` #[unstable(feature = "internal_impls_macro", issue = "none")] +// Allow implementations of `UnsizedConstParamTy` even though std cannot use that feature. +#[allow_internal_unstable(unsized_const_params)] macro marker_impls { ( $(#[$($meta:tt)*])* $Trait:ident for $({$($bounds:tt)*})? $T:ty $(, $($rest:tt)*)? ) => { $(#[$($meta)*])* impl< $($($bounds)*)? > $Trait for $T {} diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index 03d9fc0a76fe1..c4ba11d0a436f 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -1,10 +1,23 @@ //@compile-flags: -Zmiri-strict-provenance -#![feature(portable_simd, adt_const_params, core_intrinsics, repr_simd)] +#![feature( + portable_simd, + unsized_const_params, + adt_const_params, + rustc_attrs, + intrinsics, + core_intrinsics, + repr_simd +)] #![allow(incomplete_features, internal_features)] use std::intrinsics::simd as intrinsics; use std::ptr; use std::simd::{prelude::*, StdFloat}; +extern "rust-intrinsic" { + #[rustc_nounwind] + pub fn simd_shuffle_generic(x: T, y: T) -> U; +} + fn simd_ops_f32() { let a = f32x4::splat(10.0); let b = f32x4::from_array([1.0, 2.0, 3.0, -4.0]); From e141b071640964cc626faa8dcf69ddcb1e24b4a7 Mon Sep 17 00:00:00 2001 From: Julius Liu Date: Thu, 25 Jul 2024 15:27:20 -0700 Subject: [PATCH 10/10] fix: compilation issue w/ refactored type --- library/std/src/sys/pal/windows/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index 15d446786addc..ea19bd1e96193 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -416,8 +416,8 @@ impl File { dwHighDateTime: (info.LastWriteTime >> 32) as u32, }, change_time: Some(c::FILETIME { - dhLowDateTime: info.ChangeTime as c::DWORD, - dhHighDateTime: (info.ChangeTime >> 32) as c::DWORD, + dwLowDateTime: info.ChangeTime as u32, + dwHighDateTime: (info.ChangeTime >> 32) as u32, }), file_size: 0, reparse_tag: 0,