From 56e152d807cb1bacd697909cb960bdc3e4cbbaa1 Mon Sep 17 00:00:00 2001 From: Nathan Henrie Date: Thu, 1 Feb 2024 09:08:52 -0700 Subject: [PATCH 01/11] Add rust-lldb pretty printing for Path and PathBuf Fixes https://github.com/rust-lang/rust/issues/120553 Fixes https://github.com/rust-lang/rust/issues/48462 --- src/etc/lldb_commands | 3 +++ src/etc/lldb_lookup.py | 5 +++++ src/etc/lldb_providers.py | 29 +++++++++++++++++++++++++++++ src/etc/rust_types.py | 6 ++++++ tests/debuginfo/path.rs | 31 +++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+) create mode 100644 tests/debuginfo/path.rs diff --git a/src/etc/lldb_commands b/src/etc/lldb_commands index 615d13ccd0ffd..4be2dba34f6f8 100644 --- a/src/etc/lldb_commands +++ b/src/etc/lldb_commands @@ -16,4 +16,7 @@ type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)R type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefMut<.+>$" --category Rust type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefCell<.+>$" --category Rust type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)NonZero<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^core::num::([a-z_]+::)*NonZero.+$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::([a-z_]+::)+)PathBuf$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust type category enable Rust diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py index 36c7d82b34ac7..a93b42e1cc449 100644 --- a/src/etc/lldb_lookup.py +++ b/src/etc/lldb_lookup.py @@ -58,6 +58,11 @@ def summary_lookup(valobj, dict): if rust_type == RustType.STD_NONZERO_NUMBER: return StdNonZeroNumberSummaryProvider(valobj, dict) + if rust_type == RustType.STD_PATHBUF: + return StdPathBufSummaryProvider(valobj, dict) + if rust_type == RustType.STD_PATH: + return StdPathSummaryProvider(valobj, dict) + return "" diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 5d2b6fd525c14..1c43977a501a0 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -173,6 +173,35 @@ def StdStrSummaryProvider(valobj, dict): return '"%s"' % data +def StdPathBufSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdPathBufSummaryProvider] for " + str(valobj.GetName()) + return StdOsStringSummaryProvider(valobj.GetChildMemberWithName("inner"), dict) + + +def StdPathSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdPathSummaryProvider] for " + str(valobj.GetName()) + length = valobj.GetChildMemberWithName("length").GetValueAsUnsigned() + if length == 0: + return '""' + + data_ptr = valobj.GetChildMemberWithName("data_ptr") + + start = data_ptr.GetValueAsUnsigned() + error = SBError() + process = data_ptr.GetProcess() + data = process.ReadMemory(start, length, error) + if PY3: + try: + data = data.decode(encoding='UTF-8') + except UnicodeDecodeError: + return '%r' % data + return '"%s"' % data + + class StructSyntheticProvider: """Pretty-printer for structs and struct enum variants""" diff --git a/src/etc/rust_types.py b/src/etc/rust_types.py index 2b06683ef93cb..c0415a3cdcfe4 100644 --- a/src/etc/rust_types.py +++ b/src/etc/rust_types.py @@ -32,6 +32,8 @@ class RustType(object): STD_REF_MUT = "StdRefMut" STD_REF_CELL = "StdRefCell" STD_NONZERO_NUMBER = "StdNonZeroNumber" + STD_PATH = "StdPath" + STD_PATHBUF = "StdPathBuf" STD_STRING_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)String$") @@ -51,6 +53,8 @@ class RustType(object): STD_REF_MUT_REGEX = re.compile(r"^(core::([a-z_]+::)+)RefMut<.+>$") STD_REF_CELL_REGEX = re.compile(r"^(core::([a-z_]+::)+)RefCell<.+>$") STD_NONZERO_NUMBER_REGEX = re.compile(r"^(core::([a-z_]+::)+)NonZero<.+>$") +STD_PATHBUF_REGEX = re.compile(r"^(std::([a-z_]+::)+)PathBuf$") +STD_PATH_REGEX = re.compile(r"^&(mut )?(std::([a-z_]+::)+)Path$") TUPLE_ITEM_REGEX = re.compile(r"__\d+$") @@ -75,6 +79,8 @@ class RustType(object): RustType.STD_REF_CELL: STD_REF_CELL_REGEX, RustType.STD_CELL: STD_CELL_REGEX, RustType.STD_NONZERO_NUMBER: STD_NONZERO_NUMBER_REGEX, + RustType.STD_PATHBUF: STD_PATHBUF_REGEX, + RustType.STD_PATH: STD_PATH_REGEX, } def is_tuple_fields(fields): diff --git a/tests/debuginfo/path.rs b/tests/debuginfo/path.rs new file mode 100644 index 0000000000000..afe923edcdd64 --- /dev/null +++ b/tests/debuginfo/path.rs @@ -0,0 +1,31 @@ +//@ ignore-gdb + +//@ compile-flags:-g + +// === LLDB TESTS ================================================================================= + +// lldb-command:run + +// lldb-command:print pathbuf +// lldb-check:[...]$0 = "/some/path" { inner = "/some/path" { inner = { inner = size=10 { [0] = '/' +// [1] = 's' [2] = 'o' [3] = 'm' [4] = 'e' [5] = '/' [6] = 'p' [7] = 'a' [8] = 't' [9] = 'h' } +// } } } +// lldb-command:po pathbuf +// lldb-check:"/some/path" +// lldb-command:print path +// lldb-check:[...]$1 = "/some/path" { data_ptr = [...] length = 10 } +// lldb-command:po path +// lldb-check:"/some/path" + +use std::path::Path; + +fn main() { + let path = Path::new("/some/path"); + let pathbuf = path.to_path_buf(); + + zzz(); // #break +} + +fn zzz() { + () +} From 6b24fdf8113d5f422bfaeff7071d8251a13468b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 21 Mar 2024 00:03:59 +0000 Subject: [PATCH 02/11] Provide structured suggestion for unconstrained generic constant ``` error: unconstrained generic constant --> $DIR/const-argument-if-length.rs:18:10 | LL | pad: [u8; is_zst::()], | ^^^^^^^^^^^^^^^^^^^ | help: try adding a `where` bound | LL | pub struct AtLeastByte where [(); is_zst::()]: { | ++++++++++++++++++++++++++ ``` Detect when the constant expression isn't `usize` and suggest casting: ``` error: unconstrained generic constant --> f300.rs:6:10 | 6 | bb::<{!N}>(); | ^^^^ -Ztrack-diagnostics: created at compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs:3539:36 | help: try adding a `where` bound | 5 | fn b() where [(); {!N} as usize]: { | ++++++++++++++++++++++++++ ``` Fix #122395. --- .../error_reporting/type_err_ctxt_ext.rs | 37 +++++++-- .../const-argument-if-length.full.stderr | 5 +- .../defaults/generic-expr-default.stderr | 10 ++- .../ensure_is_evaluatable.stderr | 5 +- .../fn_with_two_const_inputs.stderr | 5 +- .../abstract-const-as-cast-2.fixed | 21 +++++ .../abstract-const-as-cast-2.rs | 15 ++-- .../abstract-const-as-cast-2.stderr | 21 +++-- .../abstract-const-as-cast-3.stderr | 20 ++++- .../abstract-consts-as-cast-5.stderr | 5 +- ...y-size-in-generic-struct-param.full.stderr | 5 +- .../doesnt_unify_evaluatable.stderr | 5 +- .../const_kind_expr/issue_114151.stderr | 18 +++-- .../relate_binop_arg_tys.stderr | 5 +- .../const_kind_expr/relate_cast_arg_ty.stderr | 5 +- .../const_kind_expr/wf_obligation.stderr | 5 +- .../cross_crate_predicate.stderr | 20 ++++- .../dependence_lint.gce.stderr | 10 ++- .../generic_const_exprs/different-fn.stderr | 5 +- .../issue-62504.full.stderr | 5 +- .../generic_const_exprs/issue-83765.stderr | 5 +- .../generic_const_exprs/issue-85848.stderr | 5 +- .../needs_where_clause.stderr | 5 +- .../no_where_clause.stderr | 10 ++- .../unify-op-with-fn-call.stderr | 5 +- .../issues/issue-67739.full.stderr | 5 +- .../const-generics/issues/issue-71202.stderr | 78 ++++++++++--------- .../const-generics/issues/issue-84659.fixed | 13 ++++ tests/ui/const-generics/issues/issue-84659.rs | 3 +- .../const-generics/issues/issue-84659.stderr | 7 +- .../const-generics/issues/issue-90455.fixed | 13 ++++ tests/ui/const-generics/issues/issue-90455.rs | 3 +- .../const-generics/issues/issue-90455.stderr | 7 +- .../const-needs_drop-monomorphic.stderr | 5 +- .../evaluatable-bounds.fixed | 25 ++++++ .../generic-const-items/evaluatable-bounds.rs | 12 +-- .../evaluatable-bounds.stderr | 13 ++++ .../evaluatable-bounds.unconstrained.stderr | 5 +- tests/ui/simd/array-trait.stderr | 6 +- tests/ui/specialization/issue-51892.stderr | 5 +- .../variance-associated-consts.stderr | 5 +- 41 files changed, 351 insertions(+), 111 deletions(-) create mode 100644 tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.fixed create mode 100644 tests/ui/const-generics/issues/issue-84659.fixed create mode 100644 tests/ui/const-generics/issues/issue-90455.fixed create mode 100644 tests/ui/generic-const-items/evaluatable-bounds.fixed create mode 100644 tests/ui/generic-const-items/evaluatable-bounds.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 4bc3ff92a6751..5c4a48896ee56 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -3538,12 +3538,39 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mut err = self.dcx().struct_span_err(span, "unconstrained generic constant"); let const_span = self.tcx.def_span(uv.def); + + let const_ty = self.tcx.type_of(uv.def).instantiate(self.tcx, uv.args); + let cast = if const_ty != self.tcx.types.usize { " as usize" } else { "" }; + let msg = "try adding a `where` bound"; match self.tcx.sess.source_map().span_to_snippet(const_span) { - Ok(snippet) => err.help(format!( - "try adding a `where` bound using this expression: `where [(); {snippet}]:`" - )), - _ => err.help("consider adding a `where` bound using this expression"), - }; + Ok(snippet) => { + let code = format!("[(); {snippet}{cast}]:"); + let def_id = if let ObligationCauseCode::CompareImplItemObligation { + trait_item_def_id, + .. + } = obligation.cause.code() + { + trait_item_def_id.as_local() + } else { + Some(obligation.cause.body_id) + }; + if let Some(def_id) = def_id + && let Some(generics) = self.tcx.hir().get_generics(def_id) + { + err.span_suggestion_verbose( + generics.tail_span_for_predicate_suggestion(), + msg, + format!("{} {code}", generics.add_where_or_trailing_comma()), + Applicability::MaybeIncorrect, + ); + } else { + err.help(format!("{msg}: where {code}")); + }; + } + _ => { + err.help(msg); + } + }; Ok(err) } ty::ConstKind::Expr(_) => { diff --git a/tests/ui/const-generics/const-argument-if-length.full.stderr b/tests/ui/const-generics/const-argument-if-length.full.stderr index db3d88392bd57..4de98c11d2b77 100644 --- a/tests/ui/const-generics/const-argument-if-length.full.stderr +++ b/tests/ui/const-generics/const-argument-if-length.full.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | pad: [u8; is_zst::()], | ^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); is_zst::()]:` +help: try adding a `where` bound + | +LL | pub struct AtLeastByte where [(); is_zst::()]: { + | ++++++++++++++++++++++++++ error[E0277]: the size for values of type `T` cannot be known at compilation time --> $DIR/const-argument-if-length.rs:16:12 diff --git a/tests/ui/const-generics/defaults/generic-expr-default.stderr b/tests/ui/const-generics/defaults/generic-expr-default.stderr index ada1498d1c80b..909edada4cbe8 100644 --- a/tests/ui/const-generics/defaults/generic-expr-default.stderr +++ b/tests/ui/const-generics/defaults/generic-expr-default.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | pub fn needs_evaluatable_bound() -> Foo { | ^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { N + 1 }]:` +help: try adding a `where` bound + | +LL | pub fn needs_evaluatable_bound() -> Foo where [(); { N + 1 }]: { + | ++++++++++++++++++++++ error: unconstrained generic constant --> $DIR/generic-expr-default.rs:14:58 @@ -12,7 +15,10 @@ error: unconstrained generic constant LL | fn needs_evaluatable_bound_alias() -> FooAlias | ^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { N + 1 }]:` +help: try adding a `where` bound + | +LL | fn needs_evaluatable_bound_alias() -> FooAlias where [(); { N + 1 }]: + | ++++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/ensure_is_evaluatable.stderr b/tests/ui/const-generics/ensure_is_evaluatable.stderr index b9bd9160b13c2..62f8bc34f2edd 100644 --- a/tests/ui/const-generics/ensure_is_evaluatable.stderr +++ b/tests/ui/const-generics/ensure_is_evaluatable.stderr @@ -4,7 +4,6 @@ error: unconstrained generic constant LL | bar() | ^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); N + 1]:` note: required by a bound in `bar` --> $DIR/ensure_is_evaluatable.rs:15:10 | @@ -13,6 +12,10 @@ LL | fn bar() -> [(); N] LL | where LL | [(); N + 1]:, | ^^^^^ required by this bound in `bar` +help: try adding a `where` bound + | +LL | [(); M + 1]:, [(); N + 1]: + | ~~~~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/fn_with_two_const_inputs.stderr b/tests/ui/const-generics/fn_with_two_const_inputs.stderr index ec31e02f144b8..c0a913a21fd2d 100644 --- a/tests/ui/const-generics/fn_with_two_const_inputs.stderr +++ b/tests/ui/const-generics/fn_with_two_const_inputs.stderr @@ -4,7 +4,6 @@ error: unconstrained generic constant LL | bar() | ^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); N + 1]:` note: required by a bound in `bar` --> $DIR/fn_with_two_const_inputs.rs:18:10 | @@ -13,6 +12,10 @@ LL | fn bar() -> [(); N] LL | where LL | [(); N + 1]:, | ^^^^^ required by this bound in `bar` +help: try adding a `where` bound + | +LL | [(); both(N + 1, M + 1)]:, [(); N + 1]: + | ~~~~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.fixed b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.fixed new file mode 100644 index 0000000000000..929282f40e64b --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.fixed @@ -0,0 +1,21 @@ +//@ run-rustfix +#![feature(generic_const_exprs)] +#![allow(incomplete_features, dead_code)] + +struct Evaluatable {} + +struct Foo([u8; N as usize]) +//~^ ERROR unconstrained generic constant +where + Evaluatable<{N as u128}>:, [(); N as usize]:; +//~^ HELP try adding a `where` bound + +struct Foo2(Evaluatable::<{N as u128}>) where Evaluatable<{N as usize as u128 }>:, [(); {N as u128} as usize]:; +//~^ ERROR unconstrained generic constant +//~| HELP try adding a `where` bound + +struct Bar([u8; (N + 2) as usize]) where [(); (N + 1) as usize]:, [(); (N + 2) as usize]:; +//~^ ERROR unconstrained generic constant +//~| HELP try adding a `where` bound + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.rs b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.rs index 3b5b87b2b3d63..3a6c4d3545141 100644 --- a/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.rs +++ b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.rs @@ -1,20 +1,21 @@ +//@ run-rustfix #![feature(generic_const_exprs)] -#![allow(incomplete_features)] +#![allow(incomplete_features, dead_code)] struct Evaluatable {} struct Foo([u8; N as usize]) -//~^ Error: unconstrained generic constant -//~| help: try adding a `where` bound using this expression: `where [(); N as usize]:` +//~^ ERROR unconstrained generic constant where Evaluatable<{N as u128}>:; +//~^ HELP try adding a `where` bound struct Foo2(Evaluatable::<{N as u128}>) where Evaluatable<{N as usize as u128 }>:; -//~^ Error: unconstrained generic constant -//~| help: try adding a `where` bound using this expression: `where [(); {N as u128}]:` +//~^ ERROR unconstrained generic constant +//~| HELP try adding a `where` bound struct Bar([u8; (N + 2) as usize]) where [(); (N + 1) as usize]:; -//~^ Error: unconstrained generic constant -//~| help: try adding a `where` bound using this expression: `where [(); (N + 2) as usize]:` +//~^ ERROR unconstrained generic constant +//~| HELP try adding a `where` bound fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.stderr b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.stderr index 5ca04d25e556e..ce8eca8d25e19 100644 --- a/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.stderr +++ b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-2.stderr @@ -1,26 +1,35 @@ error: unconstrained generic constant - --> $DIR/abstract-const-as-cast-2.rs:6:25 + --> $DIR/abstract-const-as-cast-2.rs:7:25 | LL | struct Foo([u8; N as usize]) | ^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); N as usize]:` +help: try adding a `where` bound + | +LL | Evaluatable<{N as u128}>:, [(); N as usize]:; + | +++++++++++++++++++ error: unconstrained generic constant - --> $DIR/abstract-const-as-cast-2.rs:12:26 + --> $DIR/abstract-const-as-cast-2.rs:13:26 | LL | struct Foo2(Evaluatable::<{N as u128}>) where Evaluatable<{N as usize as u128 }>:; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); {N as u128}]:` +help: try adding a `where` bound + | +LL | struct Foo2(Evaluatable::<{N as u128}>) where Evaluatable<{N as usize as u128 }>:, [(); {N as u128} as usize]:; + | +++++++++++++++++++++++++++++ error: unconstrained generic constant - --> $DIR/abstract-const-as-cast-2.rs:16:25 + --> $DIR/abstract-const-as-cast-2.rs:17:25 | LL | struct Bar([u8; (N + 2) as usize]) where [(); (N + 1) as usize]:; | ^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); (N + 2) as usize]:` +help: try adding a `where` bound + | +LL | struct Bar([u8; (N + 2) as usize]) where [(); (N + 1) as usize]:, [(); (N + 2) as usize]:; + | +++++++++++++++++++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr index bd6fd67b89d74..3622ef16a9608 100644 --- a/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr +++ b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr @@ -4,7 +4,6 @@ error: unconstrained generic constant LL | assert_impl::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { O as u128 }]:` note: required for `HasCastInTraitImpl<{ N + 1 }, { N as u128 }>` to implement `Trait` --> $DIR/abstract-const-as-cast-3.rs:8:22 | @@ -15,6 +14,10 @@ note: required by a bound in `use_trait_impl::assert_impl` | LL | fn assert_impl() {} | ^^^^^ required by this bound in `assert_impl` +help: try adding a `where` bound + | +LL | EvaluatableU128<{N as u128}>:, [(); { O as u128 } as usize]: { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0308]: mismatched types --> $DIR/abstract-const-as-cast-3.rs:17:5 @@ -36,7 +39,6 @@ error: unconstrained generic constant LL | assert_impl::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { O as u128 }]:` note: required for `HasCastInTraitImpl<{ N + 1 }, { N as _ }>` to implement `Trait` --> $DIR/abstract-const-as-cast-3.rs:8:22 | @@ -47,6 +49,10 @@ note: required by a bound in `use_trait_impl::assert_impl` | LL | fn assert_impl() {} | ^^^^^ required by this bound in `assert_impl` +help: try adding a `where` bound + | +LL | EvaluatableU128<{N as u128}>:, [(); { O as u128 } as usize]: { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0308]: mismatched types --> $DIR/abstract-const-as-cast-3.rs:20:5 @@ -96,7 +102,6 @@ error: unconstrained generic constant LL | assert_impl::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { O as u128 }]:` note: required for `HasCastInTraitImpl<{ N + 1 }, { N as u128 }>` to implement `Trait` --> $DIR/abstract-const-as-cast-3.rs:8:22 | @@ -107,6 +112,10 @@ note: required by a bound in `use_trait_impl_2::assert_impl` | LL | fn assert_impl() {} | ^^^^^ required by this bound in `assert_impl` +help: try adding a `where` bound + | +LL | EvaluatableU128<{N as _}>:, [(); { O as u128 } as usize]: { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0308]: mismatched types --> $DIR/abstract-const-as-cast-3.rs:35:5 @@ -128,7 +137,6 @@ error: unconstrained generic constant LL | assert_impl::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { O as u128 }]:` note: required for `HasCastInTraitImpl<{ N + 1 }, { N as _ }>` to implement `Trait` --> $DIR/abstract-const-as-cast-3.rs:8:22 | @@ -139,6 +147,10 @@ note: required by a bound in `use_trait_impl_2::assert_impl` | LL | fn assert_impl() {} | ^^^^^ required by this bound in `assert_impl` +help: try adding a `where` bound + | +LL | EvaluatableU128<{N as _}>:, [(); { O as u128 } as usize]: { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0308]: mismatched types --> $DIR/abstract-const-as-cast-3.rs:38:5 diff --git a/tests/ui/const-generics/generic_const_exprs/abstract-consts-as-cast-5.stderr b/tests/ui/const-generics/generic_const_exprs/abstract-consts-as-cast-5.stderr index 4b76ae6cfd56d..5fc36ebc92927 100644 --- a/tests/ui/const-generics/generic_const_exprs/abstract-consts-as-cast-5.stderr +++ b/tests/ui/const-generics/generic_const_exprs/abstract-consts-as-cast-5.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | bar::<{ N as usize as usize }>(); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { N as usize as usize }]:` +help: try adding a `where` bound + | +LL | fn foo(a: [(); N as usize]) where [(); { N as usize as usize }]: { + | ++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr index c478718b4cc79..471b850d8d544 100644 --- a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr +++ b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | struct ArithArrayLen([u32; 0 + N]); | ^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); 0 + N]:` +help: try adding a `where` bound + | +LL | struct ArithArrayLen([u32; 0 + N]) where [(); 0 + N]:; + | ++++++++++++++++++ error: overly complex generic constant --> $DIR/array-size-in-generic-struct-param.rs:23:15 diff --git a/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr b/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr index a8657bf5263a8..f3a38fcc00544 100644 --- a/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr +++ b/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | bar::<{ T::ASSOC }>(); | ^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { T::ASSOC }]:` +help: try adding a `where` bound + | +LL | fn foo() where [(); U::ASSOC]:, [(); { T::ASSOC }]: { + | ~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr index 9a8aa222dc1cc..0c29d94ed5b4a 100644 --- a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr @@ -24,7 +24,10 @@ error: unconstrained generic constant LL | foo::<_, L>([(); L + 1 + L]); | ^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); L + 1 + L]:` +help: try adding a `where` bound + | +LL | [(); (L - 1) + 1 + L]:, [(); L + 1 + L]: + | ~~~~~~~~~~~~~~~~~~ error: unconstrained generic constant --> $DIR/issue_114151.rs:17:17 @@ -34,11 +37,6 @@ LL | foo::<_, L>([(); L + 1 + L]); | | | required by a bound introduced by this call | - = help: try adding a `where` bound using this expression: `where [(); { - { - N - } - }]:` note: required by a bound in `foo` --> $DIR/issue_114151.rs:5:13 | @@ -51,6 +49,14 @@ LL | | N LL | | } LL | | }], | |_____^ required by this bound in `foo` +help: try adding a `where` bound + | +LL ~ [(); (L - 1) + 1 + L]:, [(); { +LL + { +LL + N +LL + } +LL + }]: + | error: unconstrained generic constant `L + 1 + L` --> $DIR/issue_114151.rs:17:5 diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_binop_arg_tys.stderr b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_binop_arg_tys.stderr index ba824e84a5aca..6a62c0b4778bd 100644 --- a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_binop_arg_tys.stderr +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_binop_arg_tys.stderr @@ -13,7 +13,10 @@ error: unconstrained generic constant LL | Bar::<{ make_generic(N, 1_u8 == 0_u8) }> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { make_generic(N, 1_u8 == 0_u8) }]:` +help: try adding a `where` bound + | +LL | fn foo() -> Bar<{ make_generic(N, true == false) }> where [(); { make_generic(N, 1_u8 == 0_u8) } as usize]: { + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_cast_arg_ty.stderr b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_cast_arg_ty.stderr index d3ba870a2d7cf..fba8b9e7784d9 100644 --- a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_cast_arg_ty.stderr +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_cast_arg_ty.stderr @@ -13,7 +13,10 @@ error: unconstrained generic constant LL | [(); (1_u8 as usize) + N] | ^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); (1_u8 as usize) + N]:` +help: try adding a `where` bound + | +LL | fn foo() -> [(); (true as usize) + N] where [(); (1_u8 as usize) + N]: { + | ++++++++++++++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr index da5194696e657..99eab935a094c 100644 --- a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr @@ -13,7 +13,10 @@ error: unconstrained generic constant LL | foo::<_, L>([(); L + 1 + L]); | ^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); L + 1 + L]:` +help: try adding a `where` bound + | +LL | [(); (L - 1) + 1 + L]:, [(); L + 1 + L]: + | ~~~~~~~~~~~~~~~~~~ error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr b/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr index 921314f0c5044..a05aaf2af649e 100644 --- a/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr +++ b/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr @@ -4,7 +4,6 @@ error: unconstrained generic constant LL | let _ = const_evaluatable_lib::test1::(); | ^ | - = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::() - 1]:` note: required by a bound in `test1` --> $DIR/auxiliary/const_evaluatable_lib.rs:5:10 | @@ -13,6 +12,10 @@ LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] LL | where LL | [u8; std::mem::size_of::() - 1]: Sized, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1` +help: try adding a `where` bound + | +LL | fn user() where [(); std::mem::size_of::() - 1]: { + | +++++++++++++++++++++++++++++++++++++++++ error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:44 @@ -20,12 +23,15 @@ error: unconstrained generic constant LL | let _ = const_evaluatable_lib::test1::(); | ^ | - = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::() - 1]:` note: required by a bound in `test1` --> $DIR/auxiliary/const_evaluatable_lib.rs:3:27 | LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1` +help: try adding a `where` bound + | +LL | fn user() where [(); std::mem::size_of::() - 1]: { + | +++++++++++++++++++++++++++++++++++++++++ error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 @@ -33,7 +39,6 @@ error: unconstrained generic constant LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::() - 1]:` note: required by a bound in `test1` --> $DIR/auxiliary/const_evaluatable_lib.rs:5:10 | @@ -42,6 +47,10 @@ LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] LL | where LL | [u8; std::mem::size_of::() - 1]: Sized, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1` +help: try adding a `where` bound + | +LL | fn user() where [(); std::mem::size_of::() - 1]: { + | +++++++++++++++++++++++++++++++++++++++++ error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 @@ -49,12 +58,15 @@ error: unconstrained generic constant LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::() - 1]:` note: required by a bound in `test1` --> $DIR/auxiliary/const_evaluatable_lib.rs:3:27 | LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1` +help: try adding a `where` bound + | +LL | fn user() where [(); std::mem::size_of::() - 1]: { + | +++++++++++++++++++++++++++++++++++++++++ error: aborting due to 4 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr b/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr index 74111ef1d38cd..632ece0ddcb30 100644 --- a/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr +++ b/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr @@ -20,7 +20,10 @@ error: unconstrained generic constant LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); size_of::<*mut T>()]:` +help: try adding a `where` bound + | +LL | fn foo() where [(); size_of::<*mut T>()]: { + | ++++++++++++++++++++++++++++++++ error: unconstrained generic constant --> $DIR/dependence_lint.rs:10:9 @@ -28,7 +31,10 @@ error: unconstrained generic constant LL | [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs` | ^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); size_of::<*mut T>()]:` +help: try adding a `where` bound + | +LL | fn foo() where [(); size_of::<*mut T>()]: { + | ++++++++++++++++++++++++++++++++ error: aborting due to 4 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/different-fn.stderr b/tests/ui/const-generics/generic_const_exprs/different-fn.stderr index 83a2f3740b146..52917df0da157 100644 --- a/tests/ui/const-generics/generic_const_exprs/different-fn.stderr +++ b/tests/ui/const-generics/generic_const_exprs/different-fn.stderr @@ -13,7 +13,10 @@ error: unconstrained generic constant LL | [0; size_of::>()] | ^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); size_of::>()]:` +help: try adding a `where` bound + | +LL | fn test() -> [u8; size_of::()] where [(); size_of::>()]: { + | ++++++++++++++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr index 87e26ce85dcfd..f27d52a1437f5 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr @@ -13,7 +13,10 @@ error: unconstrained generic constant LL | ArrayHolder([0; Self::SIZE]) | ^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); Self::SIZE]:` +help: try adding a `where` bound + | +LL | pub const fn new() -> Self where [(); Self::SIZE]: { + | +++++++++++++++++++++++ error[E0282]: type annotations needed for `ArrayHolder` --> $DIR/issue-62504.rs:26:9 diff --git a/tests/ui/const-generics/generic_const_exprs/issue-83765.stderr b/tests/ui/const-generics/generic_const_exprs/issue-83765.stderr index b693023f125a4..ca6681b635a4a 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-83765.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-83765.stderr @@ -13,12 +13,15 @@ error: unconstrained generic constant LL | self.reference.size() | ^^^^ | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` note: required by a bound in `TensorSize::size` --> $DIR/issue-83765.rs:9:31 | LL | fn size(&self) -> [usize; Self::DIM]; | ^^^^^^^^^ required by this bound in `TensorSize::size` +help: try adding a `where` bound + | +LL | fn size(&self) -> [usize; DIM] where [(); Self::DIM]: { + | ++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/issue-83765.rs:32:9 diff --git a/tests/ui/const-generics/generic_const_exprs/issue-85848.stderr b/tests/ui/const-generics/generic_const_exprs/issue-85848.stderr index 3acccba026f26..4abe39eb598c8 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-85848.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-85848.stderr @@ -35,7 +35,6 @@ LL | writes_to_specific_path(&cap); | | | required by a bound introduced by this call | - = help: try adding a `where` bound using this expression: `where [(); { contains::() }]:` note: required for `&C` to implement `Contains<(), true>` --> $DIR/issue-85848.rs:21:12 | @@ -53,6 +52,10 @@ note: required by a bound in `writes_to_specific_path` | LL | fn writes_to_specific_path>(cap: &C) {} | ^^^^^^^^^^^^^ required by this bound in `writes_to_specific_path` +help: try adding a `where` bound + | +LL | fn writes_to_path(cap: &C) where [(); { contains::() } as usize]: { + | ++++++++++++++++++++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/issue-85848.rs:24:5 diff --git a/tests/ui/const-generics/generic_const_exprs/needs_where_clause.stderr b/tests/ui/const-generics/generic_const_exprs/needs_where_clause.stderr index 395088bf2f29c..c83e859ac7972 100644 --- a/tests/ui/const-generics/generic_const_exprs/needs_where_clause.stderr +++ b/tests/ui/const-generics/generic_const_exprs/needs_where_clause.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | b: [f32; complex_maths::(N)], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); complex_maths::(N)]:` +help: try adding a `where` bound + | +LL | struct Example where [(); complex_maths::(N)]: { + | ++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/generic_const_exprs/no_where_clause.stderr b/tests/ui/const-generics/generic_const_exprs/no_where_clause.stderr index 4a87649f43de9..a2680eac0398d 100644 --- a/tests/ui/const-generics/generic_const_exprs/no_where_clause.stderr +++ b/tests/ui/const-generics/generic_const_exprs/no_where_clause.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | b: [f32; complex_maths(N)], | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); complex_maths(N)]:` +help: try adding a `where` bound + | +LL | pub struct Example where [(); complex_maths(N)]: { + | +++++++++++++++++++++++++++++ error: unconstrained generic constant --> $DIR/no_where_clause.rs:18:15 @@ -12,7 +15,10 @@ error: unconstrained generic constant LL | b: [0.; complex_maths(N)], | ^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); complex_maths(N)]:` +help: try adding a `where` bound + | +LL | pub fn new() -> Self where [(); complex_maths(N)]: { + | +++++++++++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr b/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr index 0bf99bb8b2641..335130c958f0e 100644 --- a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr +++ b/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr @@ -40,7 +40,10 @@ error: unconstrained generic constant LL | bar2::<{ std::ops::Add::add(N, N) }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { std::ops::Add::add(N, N) }]:` +help: try adding a `where` bound + | +LL | fn foo2(a: Evaluatable2<{ N + N }>) where [(); { std::ops::Add::add(N, N) }]: { + | +++++++++++++++++++++++++++++++++++++++++ error[E0015]: cannot call non-const operator in constants --> $DIR/unify-op-with-fn-call.rs:20:39 diff --git a/tests/ui/const-generics/issues/issue-67739.full.stderr b/tests/ui/const-generics/issues/issue-67739.full.stderr index bdf05023d5f0b..99368a6614855 100644 --- a/tests/ui/const-generics/issues/issue-67739.full.stderr +++ b/tests/ui/const-generics/issues/issue-67739.full.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | [0u8; mem::size_of::()]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); mem::size_of::()]:` +help: try adding a `where` bound + | +LL | fn associated_size(&self) -> usize where [(); mem::size_of::()]: { + | +++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/issues/issue-71202.stderr b/tests/ui/const-generics/issues/issue-71202.stderr index 437b808c893c2..a2d382218526e 100644 --- a/tests/ui/const-generics/issues/issue-71202.stderr +++ b/tests/ui/const-generics/issues/issue-71202.stderr @@ -10,24 +10,27 @@ LL | | >::VALUE LL | | } as usize] = []; | |_____________________^ | - = help: try adding a `where` bound using this expression: `where [(); 1 - { - trait NotCopy { - const VALUE: bool = false; - } - - impl<__Type: ?Sized> NotCopy for __Type {} - - struct IsCopy<__Type: ?Sized>(PhantomData<__Type>); - - impl<__Type> IsCopy<__Type> - where - __Type: Sized + Copy, - { - const VALUE: bool = true; - } - - >::VALUE - } as usize]:` +help: try adding a `where` bound + | +LL ~ } as usize] where [(); 1 - { +LL + trait NotCopy { +LL + const VALUE: bool = false; +LL + } +LL + +LL + impl<__Type: ?Sized> NotCopy for __Type {} +LL + +LL + struct IsCopy<__Type: ?Sized>(PhantomData<__Type>); +LL + +LL + impl<__Type> IsCopy<__Type> +LL + where +LL + __Type: Sized + Copy, +LL + { +LL + const VALUE: bool = true; +LL + } +LL + +LL + >::VALUE +LL ~ } as usize]: = []; + | error: unconstrained generic constant --> $DIR/issue-71202.rs:28:19 @@ -35,24 +38,27 @@ error: unconstrained generic constant LL | } as usize] = []; | ^^ | - = help: try adding a `where` bound using this expression: `where [(); 1 - { - trait NotCopy { - const VALUE: bool = false; - } - - impl<__Type: ?Sized> NotCopy for __Type {} - - struct IsCopy<__Type: ?Sized>(PhantomData<__Type>); - - impl<__Type> IsCopy<__Type> - where - __Type: Sized + Copy, - { - const VALUE: bool = true; - } - - >::VALUE - } as usize]:` +help: try adding a `where` bound + | +LL ~ } as usize] where [(); 1 - { +LL + trait NotCopy { +LL + const VALUE: bool = false; +LL + } +LL + +LL + impl<__Type: ?Sized> NotCopy for __Type {} +LL + +LL + struct IsCopy<__Type: ?Sized>(PhantomData<__Type>); +LL + +LL + impl<__Type> IsCopy<__Type> +LL + where +LL + __Type: Sized + Copy, +LL + { +LL + const VALUE: bool = true; +LL + } +LL + +LL + >::VALUE +LL ~ } as usize]: = []; + | error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/issues/issue-84659.fixed b/tests/ui/const-generics/issues/issue-84659.fixed new file mode 100644 index 0000000000000..85f1adc5c19dc --- /dev/null +++ b/tests/ui/const-generics/issues/issue-84659.fixed @@ -0,0 +1,13 @@ +//@ run-rustfix +#![allow(incomplete_features, dead_code, unused_braces)] +#![feature(generic_const_exprs)] + +trait Bar {} + +trait Foo<'a> { + const N: usize; + type Baz: Bar<{ Self::N }> where [(); { Self::N }]:; + //~^ ERROR: unconstrained generic constant +} + +fn main() {} diff --git a/tests/ui/const-generics/issues/issue-84659.rs b/tests/ui/const-generics/issues/issue-84659.rs index 440ca740af2b0..b2ea6320f73bc 100644 --- a/tests/ui/const-generics/issues/issue-84659.rs +++ b/tests/ui/const-generics/issues/issue-84659.rs @@ -1,4 +1,5 @@ -#![allow(incomplete_features)] +//@ run-rustfix +#![allow(incomplete_features, dead_code, unused_braces)] #![feature(generic_const_exprs)] trait Bar {} diff --git a/tests/ui/const-generics/issues/issue-84659.stderr b/tests/ui/const-generics/issues/issue-84659.stderr index 796c5515e0458..82e80603c7f5c 100644 --- a/tests/ui/const-generics/issues/issue-84659.stderr +++ b/tests/ui/const-generics/issues/issue-84659.stderr @@ -1,10 +1,13 @@ error: unconstrained generic constant - --> $DIR/issue-84659.rs:8:15 + --> $DIR/issue-84659.rs:9:15 | LL | type Baz: Bar<{ Self::N }>; | ^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { Self::N }]:` +help: try adding a `where` bound + | +LL | type Baz: Bar<{ Self::N }> where [(); { Self::N }]:; + | ++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/issues/issue-90455.fixed b/tests/ui/const-generics/issues/issue-90455.fixed new file mode 100644 index 0000000000000..2502d47eb4628 --- /dev/null +++ b/tests/ui/const-generics/issues/issue-90455.fixed @@ -0,0 +1,13 @@ +//@ run-rustfix +#![feature(generic_const_exprs, adt_const_params)] +#![allow(incomplete_features, dead_code)] + +struct FieldElement where [(); num_limbs(N)]: { + n: [u64; num_limbs(N)], + //~^ ERROR unconstrained generic constant +} +const fn num_limbs(_: &str) -> usize { + 0 +} + +fn main() {} diff --git a/tests/ui/const-generics/issues/issue-90455.rs b/tests/ui/const-generics/issues/issue-90455.rs index a580410cf37ef..794c7d76cb1f0 100644 --- a/tests/ui/const-generics/issues/issue-90455.rs +++ b/tests/ui/const-generics/issues/issue-90455.rs @@ -1,5 +1,6 @@ +//@ run-rustfix #![feature(generic_const_exprs, adt_const_params)] -#![allow(incomplete_features)] +#![allow(incomplete_features, dead_code)] struct FieldElement { n: [u64; num_limbs(N)], diff --git a/tests/ui/const-generics/issues/issue-90455.stderr b/tests/ui/const-generics/issues/issue-90455.stderr index 1db906095727e..1fcc08db579be 100644 --- a/tests/ui/const-generics/issues/issue-90455.stderr +++ b/tests/ui/const-generics/issues/issue-90455.stderr @@ -1,10 +1,13 @@ error: unconstrained generic constant - --> $DIR/issue-90455.rs:5:8 + --> $DIR/issue-90455.rs:6:8 | LL | n: [u64; num_limbs(N)], | ^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); num_limbs(N)]:` +help: try adding a `where` bound + | +LL | struct FieldElement where [(); num_limbs(N)]: { + | +++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-needs_drop-monomorphic.stderr b/tests/ui/consts/const-needs_drop-monomorphic.stderr index 0874a70ce392e..446d34810c59c 100644 --- a/tests/ui/consts/const-needs_drop-monomorphic.stderr +++ b/tests/ui/consts/const-needs_drop-monomorphic.stderr @@ -13,7 +13,10 @@ error: unconstrained generic constant LL | Bool::<{ std::mem::needs_drop::() }>::assert(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); { std::mem::needs_drop::() }]:` +help: try adding a `where` bound + | +LL | fn f() where [(); { std::mem::needs_drop::() } as usize]: { + | +++++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/generic-const-items/evaluatable-bounds.fixed b/tests/ui/generic-const-items/evaluatable-bounds.fixed new file mode 100644 index 0000000000000..de1baffbcdbaa --- /dev/null +++ b/tests/ui/generic-const-items/evaluatable-bounds.fixed @@ -0,0 +1,25 @@ +// This is a regression test for issue #104400. + +//@ run-rustfix + +// Test that we can constrain generic const items that appear inside associated consts by +// adding a (makeshift) "evaluatable"-bound to the item, after applying the suggestion. + +#![feature(generic_const_items, generic_const_exprs)] +#![allow(incomplete_features)] + +trait Trait { + const LEN: usize; + + const ARRAY: [i32; Self::LEN] where [(); Self::LEN]:; //~ ERROR unconstrained generic constant + +} + +impl Trait for () { + const LEN: usize = 2; + const ARRAY: [i32; Self::LEN] = [360, 720]; +} + +fn main() { + let [_, _] = <() as Trait>::ARRAY; +} diff --git a/tests/ui/generic-const-items/evaluatable-bounds.rs b/tests/ui/generic-const-items/evaluatable-bounds.rs index 1a858f4199923..b47801360f9af 100644 --- a/tests/ui/generic-const-items/evaluatable-bounds.rs +++ b/tests/ui/generic-const-items/evaluatable-bounds.rs @@ -1,10 +1,9 @@ // This is a regression test for issue #104400. -//@ revisions: unconstrained constrained -//@[constrained] check-pass +//@ run-rustfix // Test that we can constrain generic const items that appear inside associated consts by -// adding a (makeshift) "evaluatable"-bound to the item. +// adding a (makeshift) "evaluatable"-bound to the item, after applying the suggestion. #![feature(generic_const_items, generic_const_exprs)] #![allow(incomplete_features)] @@ -12,13 +11,8 @@ trait Trait { const LEN: usize; - #[cfg(unconstrained)] - const ARRAY: [i32; Self::LEN]; //[unconstrained]~ ERROR unconstrained generic constant + const ARRAY: [i32; Self::LEN]; //~ ERROR unconstrained generic constant - #[cfg(constrained)] - const ARRAY: [i32; Self::LEN] - where - [(); Self::LEN]:; } impl Trait for () { diff --git a/tests/ui/generic-const-items/evaluatable-bounds.stderr b/tests/ui/generic-const-items/evaluatable-bounds.stderr new file mode 100644 index 0000000000000..ca26d6336588d --- /dev/null +++ b/tests/ui/generic-const-items/evaluatable-bounds.stderr @@ -0,0 +1,13 @@ +error: unconstrained generic constant + --> $DIR/evaluatable-bounds.rs:14:5 + | +LL | const ARRAY: [i32; Self::LEN]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try adding a `where` bound + | +LL | const ARRAY: [i32; Self::LEN] where [(); Self::LEN]:; + | ++++++++++++++++++++++ + +error: aborting due to 1 previous error + diff --git a/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr b/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr index 1475d988ea482..b6f9bdce1cb7a 100644 --- a/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr +++ b/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | const ARRAY: [i32; Self::LEN]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); Self::LEN]:` +help: try adding a `where` bound + | +LL | const ARRAY: [i32; Self::LEN] where [(); Self::LEN]:; + | ++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/simd/array-trait.stderr b/tests/ui/simd/array-trait.stderr index 16ff732396dd4..a63dbf37959f8 100644 --- a/tests/ui/simd/array-trait.stderr +++ b/tests/ui/simd/array-trait.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | pub struct T([S::Lane; S::SIZE]); | ^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); S::SIZE]:` +help: try adding a `where` bound + | +LL | pub struct T([S::Lane; S::SIZE]) where [(); S::SIZE]:; + | ++++++++++++++++++++ error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type --> $DIR/array-trait.rs:23:1 @@ -20,7 +23,6 @@ LL | #[derive(Copy, Clone)] LL | pub struct T([S::Lane; S::SIZE]); | ^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); S::SIZE]:` = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/specialization/issue-51892.stderr b/tests/ui/specialization/issue-51892.stderr index 9553a04c8f6bf..b1cabc0ac0e4a 100644 --- a/tests/ui/specialization/issue-51892.stderr +++ b/tests/ui/specialization/issue-51892.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | type Type = [u8; std::mem::size_of::<::Type>()]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::<::Type>()]:` +help: try adding a `where` bound + | +LL | type Type = [u8; std::mem::size_of::<::Type>()] where [(); std::mem::size_of::<::Type>()]:; + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/variance/variance-associated-consts.stderr b/tests/ui/variance/variance-associated-consts.stderr index f41574ca3a37b..b955a7686c2a7 100644 --- a/tests/ui/variance/variance-associated-consts.stderr +++ b/tests/ui/variance/variance-associated-consts.stderr @@ -4,7 +4,10 @@ error: unconstrained generic constant LL | field: [u8; ::Const] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try adding a `where` bound using this expression: `where [(); ::Const]:` +help: try adding a `where` bound + | +LL | struct Foo where [(); ::Const]: { + | ++++++++++++++++++++++++++++++++ error: [o] --> $DIR/variance-associated-consts.rs:13:1 From fb65ca14b21cb3210251915864007d7c0d64ae21 Mon Sep 17 00:00:00 2001 From: Giacomo Stevanato Date: Sun, 24 Mar 2024 11:20:10 +0100 Subject: [PATCH 03/11] Clarify transmute example --- library/core/src/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index dec31548fc8c3..bb1debfc77dec 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1356,7 +1356,7 @@ extern "rust-intrinsic" { /// let v_clone = v_orig.clone(); /// /// // This is the suggested, safe way. - /// // It does copy the entire vector, though, into a new array. + /// // It may copy the entire vector into a new one though, but also may not. /// let v_collected = v_clone.into_iter() /// .map(Some) /// .collect::>>(); From b5ee20f714456c027b25539f8b145152b3f0e2b1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 24 Mar 2024 22:00:08 +1100 Subject: [PATCH 04/11] Clean up unnecessary headers/flags in coverage mir-opt tests These headers and flags were historically needed, but are now unnecessary due to various changes in how coverage information is stored in MIR. --- ...ument_coverage.bar.InstrumentCoverage.diff | 2 +- ...ment_coverage.main.InstrumentCoverage.diff | 10 +++---- tests/mir-opt/instrument_coverage.rs | 30 +++++++------------ ...rage_cleanup.main.CleanupPostBorrowck.diff | 12 ++++---- ...erage_cleanup.main.InstrumentCoverage.diff | 12 ++++---- tests/mir-opt/instrument_coverage_cleanup.rs | 1 - 6 files changed, 28 insertions(+), 39 deletions(-) diff --git a/tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff b/tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff index dec99ff4601a0..01b01ea5c1756 100644 --- a/tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff +++ b/tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff @@ -4,7 +4,7 @@ fn bar() -> bool { let mut _0: bool; -+ coverage Code(Counter(0)) => /the/src/instrument_coverage.rs:21:1 - 23:2; ++ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:19:1 - 21:2; + bb0: { + Coverage::CounterIncrement(0); diff --git a/tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff b/tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff index 368088d12961a..3606a9e393231 100644 --- a/tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff +++ b/tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff @@ -9,11 +9,11 @@ + coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Add, rhs: Counter(1) }; + coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Subtract, rhs: Counter(1) }; -+ coverage Code(Counter(0)) => /the/src/instrument_coverage.rs:12:1 - 12:11; -+ coverage Code(Expression(0)) => /the/src/instrument_coverage.rs:13:5 - 14:17; -+ coverage Code(Expression(1)) => /the/src/instrument_coverage.rs:15:13 - 15:18; -+ coverage Code(Counter(1)) => /the/src/instrument_coverage.rs:16:10 - 16:11; -+ coverage Code(Expression(1)) => /the/src/instrument_coverage.rs:18:1 - 18:2; ++ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:10:1 - 10:11; ++ coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:11:5 - 12:17; ++ coverage Code(Expression(1)) => $DIR/instrument_coverage.rs:13:13 - 13:18; ++ coverage Code(Counter(1)) => $DIR/instrument_coverage.rs:14:10 - 14:11; ++ coverage Code(Expression(1)) => $DIR/instrument_coverage.rs:16:1 - 16:2; + bb0: { + Coverage::CounterIncrement(0); diff --git a/tests/mir-opt/instrument_coverage.rs b/tests/mir-opt/instrument_coverage.rs index 010f7bf4d80b2..ae63990f253df 100644 --- a/tests/mir-opt/instrument_coverage.rs +++ b/tests/mir-opt/instrument_coverage.rs @@ -1,11 +1,9 @@ -// skip-filecheck -// Test that `-C instrument-coverage` injects Coverage statements. The Coverage Counter statements -// are later converted into LLVM instrprof.increment intrinsics, during codegen. +// Test that `-C instrument-coverage` injects Coverage statements. +// The Coverage::CounterIncrement statements are later converted into LLVM +// instrprof.increment intrinsics, during codegen. //@ unit-test: InstrumentCoverage -//@ needs-profiler-support -//@ ignore-windows -//@ compile-flags: -C instrument-coverage --remap-path-prefix={{src-base}}=/the/src +//@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime // EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff // EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff @@ -22,17 +20,9 @@ fn bar() -> bool { true } -// Note that the MIR with injected coverage intrinsics includes references to source locations, -// including the source file absolute path. Typically, MIR pretty print output with file -// references are safe because the file prefixes are substituted with `$DIR`, but in this case -// the file references are encoded as function arguments, with an `Operand` type representation -// (`Slice` `Allocation` interned byte array) that cannot be normalized by simple substitution. -// -// The first workaround is to use the `SourceMap`-supported `--remap-path-prefix` option; however, -// the implementation of the `--remap-path-prefix` option currently joins the new prefix and the -// remaining source path with an OS-specific path separator (`\` on Windows). This difference still -// shows up in the byte array representation of the path, causing Windows tests to fail to match -// blessed results baselined with a `/` path separator. -// -// Since this `mir-opt` test does not have any significant platform dependencies, other than the -// path separator differences, the final workaround is to disable testing on Windows. +// CHECK: coverage ExpressionId({{[0-9]+}}) => +// CHECK-DAG: coverage Code(Counter({{[0-9]+}})) => +// CHECK-DAG: coverage Code(Expression({{[0-9]+}})) => +// CHECK: bb0: +// CHECK-DAG: Coverage::ExpressionUsed({{[0-9]+}}) +// CHECK-DAG: Coverage::CounterIncrement({{[0-9]+}}) diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff b/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff index ff65ca7703928..34d011540b9c1 100644 --- a/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff +++ b/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff @@ -5,15 +5,15 @@ let mut _0: (); let mut _1: bool; - coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => /the/src/instrument_coverage_cleanup.rs:15:8: 15:36 (#0) + coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) }; coverage ExpressionId(1) => Expression { lhs: Counter(1), op: Add, rhs: Expression(0) }; - coverage Code(Counter(0)) => /the/src/instrument_coverage_cleanup.rs:14:1 - 15:36; - coverage Code(Expression(0)) => /the/src/instrument_coverage_cleanup.rs:15:37 - 15:39; - coverage Code(Counter(1)) => /the/src/instrument_coverage_cleanup.rs:15:39 - 15:40; - coverage Code(Expression(1)) => /the/src/instrument_coverage_cleanup.rs:16:1 - 16:2; - coverage Branch { true_term: Expression(0), false_term: Counter(1) } => /the/src/instrument_coverage_cleanup.rs:15:8 - 15:36; + coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1 - 14:36; + coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37 - 14:39; + coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39 - 14:40; + coverage Code(Expression(1)) => $DIR/instrument_coverage_cleanup.rs:15:1 - 15:2; + coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8 - 14:36; bb0: { Coverage::CounterIncrement(0); diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff b/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff index 8757559149a43..6756d5c1e1b2c 100644 --- a/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff +++ b/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff @@ -5,15 +5,15 @@ let mut _0: (); let mut _1: bool; - coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => /the/src/instrument_coverage_cleanup.rs:15:8: 15:36 (#0) + coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) + coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) }; + coverage ExpressionId(1) => Expression { lhs: Counter(1), op: Add, rhs: Expression(0) }; -+ coverage Code(Counter(0)) => /the/src/instrument_coverage_cleanup.rs:14:1 - 15:36; -+ coverage Code(Expression(0)) => /the/src/instrument_coverage_cleanup.rs:15:37 - 15:39; -+ coverage Code(Counter(1)) => /the/src/instrument_coverage_cleanup.rs:15:39 - 15:40; -+ coverage Code(Expression(1)) => /the/src/instrument_coverage_cleanup.rs:16:1 - 16:2; -+ coverage Branch { true_term: Expression(0), false_term: Counter(1) } => /the/src/instrument_coverage_cleanup.rs:15:8 - 15:36; ++ coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1 - 14:36; ++ coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37 - 14:39; ++ coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39 - 14:40; ++ coverage Code(Expression(1)) => $DIR/instrument_coverage_cleanup.rs:15:1 - 15:2; ++ coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8 - 14:36; + bb0: { + Coverage::CounterIncrement(0); diff --git a/tests/mir-opt/instrument_coverage_cleanup.rs b/tests/mir-opt/instrument_coverage_cleanup.rs index 8a2fd67139bd9..7db76339e551d 100644 --- a/tests/mir-opt/instrument_coverage_cleanup.rs +++ b/tests/mir-opt/instrument_coverage_cleanup.rs @@ -7,7 +7,6 @@ //@ unit-test: InstrumentCoverage //@ compile-flags: -Cinstrument-coverage -Zcoverage-options=branch -Zno-profiler-runtime -//@ compile-flags: --remap-path-prefix={{src-base}}=/the/src // EMIT_MIR instrument_coverage_cleanup.main.InstrumentCoverage.diff // EMIT_MIR instrument_coverage_cleanup.main.CleanupPostBorrowck.diff From ea4518522f65480eb507a255dc215d39231e659f Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Mon, 19 Feb 2024 18:39:35 +0000 Subject: [PATCH 05/11] CFI: Handle dyn with no principal In user-facing Rust, `dyn` always has at least one predicate following it. Unfortunately, because we filter out marker traits from receivers at callsites and `dyn Sync` is, for example, legal, this results in us having `dyn` types with no predicates on occasion in our alias set encoding. This patch handles cases where there are no predicates in a `dyn` type which are relevant to its alias set. Fixes #122998 --- .../src/typeid/typeid_itanium_cxx_abi.rs | 27 ++++++++++--------- tests/ui/sanitizer/cfi-drop-no-principal.rs | 21 +++++++++++++++ 2 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 tests/ui/sanitizer/cfi-drop-no-principal.rs diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 367fec0e8fcb7..eda7d396d7240 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -747,9 +747,8 @@ fn transform_predicates<'tcx>( tcx: TyCtxt<'tcx>, predicates: &List>, ) -> &'tcx List> { - let predicates: Vec> = predicates - .iter() - .filter_map(|predicate| match predicate.skip_binder() { + tcx.mk_poly_existential_predicates_from_iter(predicates.iter().filter_map(|predicate| { + match predicate.skip_binder() { ty::ExistentialPredicate::Trait(trait_ref) => { let trait_ref = ty::TraitRef::identity(tcx, trait_ref.def_id); Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait( @@ -758,9 +757,8 @@ fn transform_predicates<'tcx>( } ty::ExistentialPredicate::Projection(..) => None, ty::ExistentialPredicate::AutoTrait(..) => Some(predicate), - }) - .collect(); - tcx.mk_poly_existential_predicates(&predicates) + } + })) } /// Transforms args for being encoded and used in the substitution dictionary. @@ -1171,14 +1169,17 @@ fn strip_receiver_auto<'tcx>( let ty::Dynamic(preds, lifetime, kind) = ty.kind() else { bug!("Tried to strip auto traits from non-dynamic type {ty}"); }; - let filtered_preds = - if preds.principal().is_some() { + let new_rcvr = if preds.principal().is_some() { + let filtered_preds = tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) - })) - } else { - ty::List::empty() - }; - let new_rcvr = Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind); + })); + Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind) + } else { + // If there's no principal type, re-encode it as a unit, since we don't know anything + // about it. This technically discards the knowledge that it was a type that was made + // into a trait object at some point, but that's not a lot. + tcx.types.unit + }; tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1)) } diff --git a/tests/ui/sanitizer/cfi-drop-no-principal.rs b/tests/ui/sanitizer/cfi-drop-no-principal.rs new file mode 100644 index 0000000000000..c1c88c8c71c73 --- /dev/null +++ b/tests/ui/sanitizer/cfi-drop-no-principal.rs @@ -0,0 +1,21 @@ +// Check that dropping a trait object without a principal trait succeeds + +//@ needs-sanitizer-cfi +// FIXME(#122848) Remove only-linux once OSX CFI binaries works +//@ only-linux +//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi +//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0 +// FIXME(#118761) Should be run-pass once the labels on drop are compatible. +// This test is being landed ahead of that to test that the compiler doesn't ICE while labeling the +// callsite for a drop, but the vtable doesn't have the correct label yet. +//@ build-pass + +struct CustomDrop; + +impl Drop for CustomDrop { + fn drop(&mut self) {} +} + +fn main() { + let _ = Box::new(CustomDrop) as Box; +} From 40f41e7e896e7d68534d05e955c20330b13f6d0b Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Sat, 23 Mar 2024 01:19:43 +0000 Subject: [PATCH 06/11] CFI: Support arbitrary receivers Previously, we only rewrote `&self` and `&mut self` receivers. By instantiating the method from the trait definition, we can make this work work with arbitrary legal receivers instead. --- Cargo.lock | 1 + compiler/rustc_symbol_mangling/Cargo.toml | 1 + compiler/rustc_symbol_mangling/src/lib.rs | 1 + .../src/typeid/typeid_itanium_cxx_abi.rs | 109 +++++++++++------- tests/ui/sanitizer/cfi-complex-receiver.rs | 42 +++++++ 5 files changed, 115 insertions(+), 39 deletions(-) create mode 100644 tests/ui/sanitizer/cfi-complex-receiver.rs diff --git a/Cargo.lock b/Cargo.lock index b8fe1ebaf8019..ad48620bd907a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4634,6 +4634,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", + "rustc_trait_selection", "tracing", "twox-hash", ] diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index 0ce522c9cabca..1c8f1d0367039 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -15,6 +15,7 @@ rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } +rustc_trait_selection = { path = "../rustc_trait_selection" } tracing = "0.1" twox-hash = "1.6.3" # tidy-alphabetical-end diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 02bb1fde75c1b..0588af9bda72a 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -90,6 +90,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(rustdoc_internals)] +#![feature(let_chains)] #![allow(internal_features)] #[macro_use] diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 367fec0e8fcb7..9406038232d1f 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -11,6 +11,7 @@ use rustc_data_structures::base_n; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{ self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind, TermKind, Ty, TyCtxt, UintTy, @@ -21,7 +22,9 @@ use rustc_span::sym; use rustc_target::abi::call::{Conv, FnAbi, PassMode}; use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits; use std::fmt::Write as _; +use std::iter; use crate::typeid::TypeIdOptions; @@ -1115,51 +1118,46 @@ pub fn typeid_for_instance<'tcx>( instance.args = strip_receiver_auto(tcx, instance.args) } + if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) + && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) + { + let impl_method = tcx.associated_item(instance.def_id()); + let method_id = impl_method + .trait_item_def_id + .expect("Part of a trait implementation, but not linked to the def_id?"); + let trait_method = tcx.associated_item(method_id); + if traits::is_vtable_safe_method(tcx, trait_ref.skip_binder().def_id, trait_method) { + // Trait methods will have a Self polymorphic parameter, where the concreteized + // implementatation will not. We need to walk back to the more general trait method + let trait_ref = tcx.instantiate_and_normalize_erasing_regions( + instance.args, + ty::ParamEnv::reveal_all(), + trait_ref, + ); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + + // At the call site, any call to this concrete function through a vtable will be + // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the + // original method id, and we've recovered the trait arguments, we can make the callee + // instance we're computing the alias set for match the caller instance. + // + // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. + // If we ever *do* start encoding the vtable index, we will need to generate an alias set + // based on which vtables we are putting this method into, as there will be more than one + // index value when supertraits are involved. + instance.def = ty::InstanceDef::Virtual(method_id, 0); + let abstract_trait_args = + tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); + } + } + let fn_abi = tcx .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty()))) .unwrap_or_else(|instance| { bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance) }); - // If this instance is a method and self is a reference, get the impl it belongs to - let impl_def_id = tcx.impl_of_method(instance.def_id()); - if impl_def_id.is_some() && !fn_abi.args.is_empty() && fn_abi.args[0].layout.ty.is_ref() { - // If this impl is not an inherent impl, get the trait it implements - if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id.unwrap()) { - // Transform the concrete self into a reference to a trait object - let existential_predicate = trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( - tcx, trait_ref, - )) - }); - let existential_predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy( - existential_predicate.skip_binder(), - )]); - // Is the concrete self mutable? - let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { - Ty::new_mut_ref( - tcx, - tcx.lifetimes.re_erased, - Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), - ) - } else { - Ty::new_imm_ref( - tcx, - tcx.lifetimes.re_erased, - Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), - ) - }; - - // Replace the concrete self in an fn_abi clone by the reference to a trait object - let mut fn_abi = fn_abi.clone(); - // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the - // other fields are never used. - fn_abi.args[0].layout.ty = self_ty; - - return typeid_for_fnabi(tcx, &fn_abi, options); - } - } - typeid_for_fnabi(tcx, fn_abi, options) } @@ -1182,3 +1180,36 @@ fn strip_receiver_auto<'tcx>( let new_rcvr = Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind); tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1)) } + +fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> { + assert!(!poly_trait_ref.has_non_region_param()); + let principal_pred = poly_trait_ref.map_bound(|trait_ref| { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)) + }); + let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref) + .flat_map(|super_poly_trait_ref| { + tcx.associated_items(super_poly_trait_ref.def_id()) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Type) + .map(move |assoc_ty| { + super_poly_trait_ref.map_bound(|super_trait_ref| { + let alias_ty = ty::AliasTy::new(tcx, assoc_ty.def_id, super_trait_ref.args); + let resolved = tcx.normalize_erasing_regions( + ty::ParamEnv::reveal_all(), + alias_ty.to_ty(tcx), + ); + ty::ExistentialPredicate::Projection(ty::ExistentialProjection { + def_id: assoc_ty.def_id, + args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args, + term: resolved.into(), + }) + }) + }) + }) + .collect(); + assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + let preds = tcx.mk_poly_existential_predicates_from_iter( + iter::once(principal_pred).chain(assoc_preds.into_iter()), + ); + Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn) +} diff --git a/tests/ui/sanitizer/cfi-complex-receiver.rs b/tests/ui/sanitizer/cfi-complex-receiver.rs new file mode 100644 index 0000000000000..c3e59258db207 --- /dev/null +++ b/tests/ui/sanitizer/cfi-complex-receiver.rs @@ -0,0 +1,42 @@ +// Check that more complex receivers work: +// * Arc as for custom receivers +// * &dyn Bar for type constraints + +//@ needs-sanitizer-cfi +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi +//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0 +//@ run-pass + +use std::sync::Arc; + +trait Foo { + fn foo(self: Arc); +} + +struct FooImpl; + +impl Foo for FooImpl { + fn foo(self: Arc) {} +} + +trait Bar { + type T; + fn bar(&self) -> Self::T; +} + +struct BarImpl; + +impl Bar for BarImpl { + type T = i32; + fn bar(&self) -> Self::T { 7 } +} + +fn main() { + let foo: Arc = Arc::new(FooImpl); + foo.foo(); + + let bar: &dyn Bar = &BarImpl; + assert_eq!(bar.bar(), 7); +} From 50b49aec3639ad59bf6afbfba25bcfcc24d7cf1c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 25 Mar 2024 10:33:54 +1100 Subject: [PATCH 07/11] Temporarily remove nnethercote from the review rotation. I will be on vacation for the next three weeks. I will re-add myself when I return. --- triagebot.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 2bcc77ae43323..a42bafe148c5f 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -775,7 +775,6 @@ compiler-team = [ compiler-team-contributors = [ "@TaKO8Ki", "@Nadrieril", - "@nnethercote", "@fmease", "@fee1-dead", ] @@ -831,17 +830,14 @@ parser = [ "@compiler-errors", "@davidtwco", "@estebank", - "@nnethercote", "@petrochenkov", "@spastorino", ] lexer = [ - "@nnethercote", "@petrochenkov", "@estebank", ] arena = [ - "@nnethercote", "@spastorino", ] mir = [ From 42066b029f3009518e2ba8c7bd20f5781b180b8f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 13 Mar 2024 15:34:05 +1100 Subject: [PATCH 08/11] Inline and remove `Parser::parse_expr_tuple_field_access_float`. It has a single call site, and afterwards all the calls to `parse_expr_tuple_field_access` are in a single method, which is nice. --- compiler/rustc_parse/src/parser/expr.rs | 74 +++++++++++++------------ 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index fe17eba0d7b8b..cf7771f87a8c9 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1005,7 +1005,44 @@ impl<'a> Parser<'a> { Ok(self.parse_expr_tuple_field_access(lo, base, symbol, suffix, None)) } token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => { - Ok(self.parse_expr_tuple_field_access_float(lo, base, symbol, suffix)) + Ok(match self.break_up_float(symbol, self.token.span) { + // 1e2 + DestructuredFloat::Single(sym, _sp) => { + self.parse_expr_tuple_field_access(lo, base, sym, suffix, None) + } + // 1. + DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { + assert!(suffix.is_none()); + self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span); + let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); + self.parse_expr_tuple_field_access(lo, base, sym, None, Some(next_token)) + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot( + symbol1, + ident1_span, + dot_span, + symbol2, + ident2_span, + ) => { + self.token = Token::new(token::Ident(symbol1, IdentIsRaw::No), ident1_span); + // This needs to be `Spacing::Alone` to prevent regressions. + // See issue #76399 and PR #76285 for more details + let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); + let base1 = self.parse_expr_tuple_field_access( + lo, + base, + symbol1, + None, + Some(next_token1), + ); + let next_token2 = + Token::new(token::Ident(symbol2, IdentIsRaw::No), ident2_span); + self.bump_with((next_token2, self.token_spacing)); // `.` + self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) + } + DestructuredFloat::Error => base, + }) } _ => { self.error_unexpected_after_dot(); @@ -1119,41 +1156,6 @@ impl<'a> Parser<'a> { } } - fn parse_expr_tuple_field_access_float( - &mut self, - lo: Span, - base: P, - float: Symbol, - suffix: Option, - ) -> P { - match self.break_up_float(float, self.token.span) { - // 1e2 - DestructuredFloat::Single(sym, _sp) => { - self.parse_expr_tuple_field_access(lo, base, sym, suffix, None) - } - // 1. - DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { - assert!(suffix.is_none()); - self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span); - let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); - self.parse_expr_tuple_field_access(lo, base, sym, None, Some(next_token)) - } - // 1.2 | 1.2e3 - DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span) => { - self.token = Token::new(token::Ident(symbol1, IdentIsRaw::No), ident1_span); - // This needs to be `Spacing::Alone` to prevent regressions. - // See issue #76399 and PR #76285 for more details - let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); - let base1 = - self.parse_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1)); - let next_token2 = Token::new(token::Ident(symbol2, IdentIsRaw::No), ident2_span); - self.bump_with((next_token2, self.token_spacing)); // `.` - self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) - } - DestructuredFloat::Error => base, - } - } - /// Parse the field access used in offset_of, matched by `$(e:expr)+`. /// Currently returns a list of idents. However, it should be possible in /// future to also do array indices, which might be arbitrary expressions. From 90eeb3d6817303469059041ef7c8c57f3508f476 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 13 Mar 2024 15:45:27 +1100 Subject: [PATCH 09/11] Remove `next_token` handling from `parse_expr_tuple_field_access`. It's clearer at the call site. --- compiler/rustc_parse/src/parser/expr.rs | 28 +++++++++---------------- 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index cf7771f87a8c9..50589ad3f1260 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1002,20 +1002,22 @@ impl<'a> Parser<'a> { match self.token.uninterpolate().kind { token::Ident(..) => self.parse_dot_suffix(base, lo), token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { - Ok(self.parse_expr_tuple_field_access(lo, base, symbol, suffix, None)) + self.bump(); + Ok(self.parse_expr_tuple_field_access(lo, base, symbol, suffix)) } token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => { Ok(match self.break_up_float(symbol, self.token.span) { // 1e2 DestructuredFloat::Single(sym, _sp) => { - self.parse_expr_tuple_field_access(lo, base, sym, suffix, None) + self.bump(); + self.parse_expr_tuple_field_access(lo, base, sym, suffix) } // 1. DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { assert!(suffix.is_none()); self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span); - let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); - self.parse_expr_tuple_field_access(lo, base, sym, None, Some(next_token)) + self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing)); + self.parse_expr_tuple_field_access(lo, base, sym, None) } // 1.2 | 1.2e3 DestructuredFloat::MiddleDot( @@ -1028,18 +1030,13 @@ impl<'a> Parser<'a> { self.token = Token::new(token::Ident(symbol1, IdentIsRaw::No), ident1_span); // This needs to be `Spacing::Alone` to prevent regressions. // See issue #76399 and PR #76285 for more details - let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); - let base1 = self.parse_expr_tuple_field_access( - lo, - base, - symbol1, - None, - Some(next_token1), - ); + self.bump_with((Token::new(token::Dot, dot_span), Spacing::Alone)); + let base1 = self.parse_expr_tuple_field_access(lo, base, symbol1, None); let next_token2 = Token::new(token::Ident(symbol2, IdentIsRaw::No), ident2_span); self.bump_with((next_token2, self.token_spacing)); // `.` - self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) + self.bump(); + self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix) } DestructuredFloat::Error => base, }) @@ -1263,12 +1260,7 @@ impl<'a> Parser<'a> { base: P, field: Symbol, suffix: Option, - next_token: Option<(Token, Spacing)>, ) -> P { - match next_token { - Some(next_token) => self.bump_with(next_token), - None => self.bump(), - } let span = self.prev_token.span; let field = ExprKind::Field(base, Ident::new(field, span)); if let Some(suffix) = suffix { From 9c091160dc57aa5224a35e6755c357f7089ff2e5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 13 Mar 2024 15:59:41 +1100 Subject: [PATCH 10/11] Change `parse_expr_tuple_field_access`. Pass in the span for the field rather than using `prev_token`. Also rename it `mk_expr_tuple_field_access`, because it doesn't do any actual parsing, it just creates an expression with what it's given. Not much of a clarity win by itself, but unlocks additional subsequent simplifications. --- compiler/rustc_parse/src/parser/expr.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 50589ad3f1260..f96e44eb0f761 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1002,22 +1002,25 @@ impl<'a> Parser<'a> { match self.token.uninterpolate().kind { token::Ident(..) => self.parse_dot_suffix(base, lo), token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { + let ident_span = self.token.span; self.bump(); - Ok(self.parse_expr_tuple_field_access(lo, base, symbol, suffix)) + Ok(self.mk_expr_tuple_field_access(lo, ident_span, base, symbol, suffix)) } token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => { Ok(match self.break_up_float(symbol, self.token.span) { // 1e2 DestructuredFloat::Single(sym, _sp) => { + let ident_span = self.token.span; self.bump(); - self.parse_expr_tuple_field_access(lo, base, sym, suffix) + self.mk_expr_tuple_field_access(lo, ident_span, base, sym, suffix) } // 1. DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { assert!(suffix.is_none()); self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span); + let ident_span = self.token.span; self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing)); - self.parse_expr_tuple_field_access(lo, base, sym, None) + self.mk_expr_tuple_field_access(lo, ident_span, base, sym, None) } // 1.2 | 1.2e3 DestructuredFloat::MiddleDot( @@ -1030,13 +1033,16 @@ impl<'a> Parser<'a> { self.token = Token::new(token::Ident(symbol1, IdentIsRaw::No), ident1_span); // This needs to be `Spacing::Alone` to prevent regressions. // See issue #76399 and PR #76285 for more details + let ident_span = self.token.span; self.bump_with((Token::new(token::Dot, dot_span), Spacing::Alone)); - let base1 = self.parse_expr_tuple_field_access(lo, base, symbol1, None); + let base1 = + self.mk_expr_tuple_field_access(lo, ident_span, base, symbol1, None); let next_token2 = Token::new(token::Ident(symbol2, IdentIsRaw::No), ident2_span); self.bump_with((next_token2, self.token_spacing)); // `.` + let ident_span = self.token.span; self.bump(); - self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix) + self.mk_expr_tuple_field_access(lo, ident_span, base1, symbol2, suffix) } DestructuredFloat::Error => base, }) @@ -1254,19 +1260,18 @@ impl<'a> Parser<'a> { Ok(fields.into_iter().collect()) } - fn parse_expr_tuple_field_access( + fn mk_expr_tuple_field_access( &mut self, lo: Span, + ident_span: Span, base: P, field: Symbol, suffix: Option, ) -> P { - let span = self.prev_token.span; - let field = ExprKind::Field(base, Ident::new(field, span)); if let Some(suffix) = suffix { - self.expect_no_tuple_index_suffix(span, suffix); + self.expect_no_tuple_index_suffix(ident_span, suffix); } - self.mk_expr(lo.to(span), field) + self.mk_expr(lo.to(ident_span), ExprKind::Field(base, Ident::new(field, ident_span))) } /// Parse a function call expression, `expr(...)`. From dce0f7f5c2a62a5aabb1d02351473d6f2b125541 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 21 Mar 2024 16:14:56 +1100 Subject: [PATCH 11/11] Clarify `parse_dot_suffix_expr`. For the `MiddleDot` case, current behaviour: - For a case like `1.2`, `sym1` is `1` and `sym2` is `2`, and `self.token` holds `1.2`. - It creates a new ident token from `sym1` that it puts into `self.token`. - Then it does `bump_with` with a new dot token, which moves the `sym1` token into `prev_token`. - Then it does `bump_with` with a new ident token from `sym2`, which moves the `dot` token into `prev_token` and discards the `sym1` token. - Then it does `bump`, which puts whatever is next into `self.token`, moves the `sym2` token into `prev_token`, and discards the `dot` token altogether. New behaviour: - Skips creating and inserting the `sym1` and dot tokens, because they are unnecessary. - This also demonstrates that the comment about `Spacing::Alone` is wrong -- that value is never used. That comment was added in #77250, and AFAICT it has always been incorrect. The commit also expands comments. I found this code hard to read previously, the examples in comments make it easier. --- compiler/rustc_parse/src/parser/expr.rs | 35 ++++++++++++++----------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f96e44eb0f761..7e317c3df1465 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -16,7 +16,6 @@ use core::mem; use core::ops::ControlFlow; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; -use rustc_ast::tokenstream::Spacing; use rustc_ast::util::case::Case; use rustc_ast::util::classify; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; @@ -999,6 +998,8 @@ impl<'a> Parser<'a> { } pub fn parse_dot_suffix_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { + // At this point we've consumed something like `expr.` and `self.token` holds the token + // after the dot. match self.token.uninterpolate().kind { token::Ident(..) => self.parse_dot_suffix(base, lo), token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { @@ -1010,39 +1011,41 @@ impl<'a> Parser<'a> { Ok(match self.break_up_float(symbol, self.token.span) { // 1e2 DestructuredFloat::Single(sym, _sp) => { + // `foo.1e2`: a single complete dot access, fully consumed. We end up with + // the `1e2` token in `self.prev_token` and the following token in + // `self.token`. let ident_span = self.token.span; self.bump(); self.mk_expr_tuple_field_access(lo, ident_span, base, sym, suffix) } // 1. DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { + // `foo.1.`: a single complete dot access and the start of another. + // We end up with the `sym` (`1`) token in `self.prev_token` and a dot in + // `self.token`. assert!(suffix.is_none()); self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span); - let ident_span = self.token.span; self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing)); self.mk_expr_tuple_field_access(lo, ident_span, base, sym, None) } // 1.2 | 1.2e3 DestructuredFloat::MiddleDot( - symbol1, + sym1, ident1_span, - dot_span, - symbol2, + _dot_span, + sym2, ident2_span, ) => { - self.token = Token::new(token::Ident(symbol1, IdentIsRaw::No), ident1_span); - // This needs to be `Spacing::Alone` to prevent regressions. - // See issue #76399 and PR #76285 for more details - let ident_span = self.token.span; - self.bump_with((Token::new(token::Dot, dot_span), Spacing::Alone)); - let base1 = - self.mk_expr_tuple_field_access(lo, ident_span, base, symbol1, None); + // `foo.1.2` (or `foo.1.2e3`): two complete dot accesses. We end up with + // the `sym2` (`2` or `2e3`) token in `self.prev_token` and the following + // token in `self.token`. let next_token2 = - Token::new(token::Ident(symbol2, IdentIsRaw::No), ident2_span); - self.bump_with((next_token2, self.token_spacing)); // `.` - let ident_span = self.token.span; + Token::new(token::Ident(sym2, IdentIsRaw::No), ident2_span); + self.bump_with((next_token2, self.token_spacing)); self.bump(); - self.mk_expr_tuple_field_access(lo, ident_span, base1, symbol2, suffix) + let base1 = + self.mk_expr_tuple_field_access(lo, ident1_span, base, sym1, None); + self.mk_expr_tuple_field_access(lo, ident2_span, base1, sym2, suffix) } DestructuredFloat::Error => base, })