Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve compile errors for invalid ptr-to-ptr casts with trait objects #130234

Merged
merged 7 commits into from
Sep 25, 2024

Conversation

lukas-code
Copy link
Member

This is a follow-up to #120248 to improve some of its error messages.

  1. Make the borrowcheck error for "type annotation requires that x must outlive y" actually point at the type annotation, i.e. the type T in a x as T cast. This makes the error more consistent with other errors caused by type annotation in other places, such as
error: lifetime may not live long enough
 --> src/lib.rs:4:12
  |
3 | fn bar(a: &i32) {
  |           - let's call the lifetime of this reference `'1`
4 |     let b: &'static i32 = a;
  |            ^^^^^^^^^^^^ type annotation requires that `'1` must outlive `'static`
  1. Don't say "cast" when we actually mean "coercion" and give borrowcheck errors from actual casts (which is currently just the check added in Make casts of pointers to trait objects stricter #120248) a higher priority than ones from coercions. This can improve the errors for ptr-to-ptr cast between trait objects because they are are lowered as an upcast "unsizing" coercion if possible (which may be the identity upcast) followed by the actual cast.

  2. Bring back the old "casting X as Y is invalid" message for type mismatch in the principals and reword the "vtable kinds may not match" message to more accurately describe the pointer metadata and not refer to "vtables" if the metadata is unknown.

fixes #130030

r? @WaffleLapkin but feel free to reassign

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 11, 2024
@rust-log-analyzer

This comment has been minimized.

compiler/rustc_hir_typeck/src/cast.rs Outdated Show resolved Hide resolved
@@ -233,8 +233,9 @@ pub enum ConstraintCategory<'tcx> {
UseAsConst,
UseAsStatic,
TypeAnnotation,
Cast {
/// Whether this is an unsizing cast and if yes, this contains the target type.
Cast,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does a cast not have unsize_to? Casts can be unsizing, for example: &() as &dyn std::any::Any.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent was to have Cast represent only the non-coercion casts from the table here1 and use Coercion for any coercion whether they are caused by a cast or not. But seeing that MIR also just represents coercions as special kind of cast and not the other way around, maybe we should just add a field is_coercion to the Cast variant and not add a new variant.

Footnotes

  1. side note: function item to function pointer and closure to function pointer should not be listed there as they are coercions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should just add a field is_coercion to the Cast variant and not add a new variant.

This seems appropriate. I think it's nice to mention what the user actually written in syntax. But trying to explain that as casts are a super set of coercions is probably confusing.

compiler/rustc_middle/src/thir/visit.rs Outdated Show resolved Hide resolved
compiler/rustc_mir_build/src/build/expr/as_place.rs Outdated Show resolved Hide resolved
compiler/rustc_mir_build/src/thir/print.rs Outdated Show resolved Hide resolved
Comment on lines 9 to 10
LL | let read = &refcell as &RefCell<dyn Read>;
| -------- cast requires that `foo` is borrowed for `'static`
| -------- coercion requires that `foo` is borrowed for `'static`
Copy link
Member

@WaffleLapkin WaffleLapkin Sep 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this and some others bellow sure look like a cast? what happened here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a side effect from how explicit coercion casts and implicit coercions are represented in the same way in MIR. I will try to recover the wording here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this turned out to be non-trivial, because there is currently no way to distinguish

let x: T = y;

from

let x = y as T

by looking at the MIR alone (without relying on the exact order of MIR statements).

So our options here are

  • Leave the diagnostic as is, calling some coercion casts just "coercions" (technically correct).
  • Revert the wording and call all coercions "casts" (technically wrong).
  • Add some additional diagnostic-only information into the MIR to allow us to distinguish them and output the optimal diagnostic (which is really just one word). This is currently unprecedented as far as I'm aware.

I'm unsure what the best option here is and would appreciate your input :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer the third option, i.e. to add some diagnostic only info. That being said I think you should check with people who work on MIR more (miri or miropt zulip streams maybe?).

The first option (using "coercion" when the user wrote a cast) could be OK. If you want we can leave the third option as a follow up (please try to do it though 😄).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After checking again, I found that we do have one instance of diagnostic information in MIR here, so this feels less unprecedented now:

/// Where this call came from in HIR/THIR.
call_source: CallSource,

tests/ui/traits/trait-object-lifetime-default-note.rs Outdated Show resolved Hide resolved
@rustbot
Copy link
Collaborator

rustbot commented Sep 15, 2024

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

This PR changes Stable MIR

cc @oli-obk, @celinval, @ouz-a

Some changes occurred in match lowering

cc @Nadrieril

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

This PR changes MIR

cc @oli-obk, @RalfJung, @JakobDegen, @davidtwco, @celinval, @vakaras

@lukas-code
Copy link
Member Author

Ah shit. Well I guess that's one way to ask for feedback about diagnostic info in MIR. 🥲

The relevant commit is 59696de.

--> $DIR/dyn-to-rigid.rs:7:5
|
LL | x as usize
| ^^^^^^^^^^
| ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This diagnostic is terrible, but that also applies to all other places that it is emitted and I'd rather postpone cleaning it up, because this PR is already getting way too large.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I support that.

@rust-log-analyzer

This comment has been minimized.

@rustbot
Copy link
Collaborator

rustbot commented Sep 15, 2024

The Miri subtree was changed

cc @rust-lang/miri

@@ -1421,6 +1419,15 @@ pub enum CastKind {
Transmute,
}

/// Represents how a [`CastKind::PointerCoercion`] was constructed, used for diagnostics.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Represents how a [`CastKind::PointerCoercion`] was constructed, used for diagnostics.
/// Represents how a [`CastKind::PointerCoercion`] was constructed.
/// Used only for diagnostics.

Copy link
Member

@WaffleLapkin WaffleLapkin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this a lot!

r=me with or without nits.

CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => {
match *cast_kind {
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => {
let is_implicit_coercion = coercion_source == CoercionSource::Implicit;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: Maybe just have a CoercionSource field in ConstraintCategory::Cast too, instead of a bool and this conversion?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered this, but decided against it, because the Ord of ConstraintCategory is relevant for the diagnostic output and I didn't want this property to spread all over the compiler.

Comment on lines -1406 to +1407
PointerCoercion(PointerCoercion),
/// Cast into a dyn* object.
DynStar,
PointerCoercion(PointerCoercion, CoercionSource),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not well versed in dyn* object, does grouping them with pointers actually make sense? cc @compiler-errors

|
LL | fn foo<'a>() {
| -- lifetime `'a` defined here
LL | let t: S<&'a isize> = S(marker::PhantomData);
LL | let a = &t as &dyn Gettable<&'a isize>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
| ^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: why does this say "type annotation" instead of "cast" like most others?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because borrow errors from type annotations are given special priority over errors from other sources (including casts) here:

ConstraintCategory::TypeAnnotation

Changing this code at all makes many other diagnostics worse, so I'd prefer to leave it alone for now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this special casing can cause some diagnostic output that is just plainly wrong, so it should definitely be changed in the future.

trait Trait<'a> {}

fn require_static(_: *const dyn Trait<'static>) {}

fn not_static<'a>(a: *const dyn Trait<'a>) {
    require_static(a as *const dyn Trait<'a>);
}
error: lifetime may not live long enough
 --> src/lib.rs:6:20
  |
5 | fn not_static<'a>(a: *const dyn Trait<'a>) {
  |               -- lifetime `'a` defined here
6 |     require_static(a as *const dyn Trait<'a>);
  |                    ^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`

@bors
Copy link
Contributor

bors commented Sep 20, 2024

☔ The latest upstream changes (presumably #130631) made this pull request unmergeable. Please resolve the merge conflicts.

Lukas Markeffsky added 7 commits September 24, 2024 22:17
This changes the remaining span for the cast, because the new `Cast`
category has a higher priority (lower `Ord`) than the old `Coercion`
category, so we no longer report the region error for the "unsizing"
coercion from `*const Trait` to itself.
@lukas-code
Copy link
Member Author

Rebased with no further changes. Per #130234 (review), I'll just go ahead and

@bors r=WaffleLapkin

Any further improvements can be landed separately.

@bors
Copy link
Contributor

bors commented Sep 24, 2024

📌 Commit b62e72c has been approved by WaffleLapkin

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 24, 2024
bors added a commit to rust-lang-ci/rust that referenced this pull request Sep 25, 2024
Rollup of 7 pull requests

Successful merges:

 - rust-lang#130234 (improve compile errors for invalid ptr-to-ptr casts with trait objects)
 - rust-lang#130752 (Improve assembly test for CMSE ABIs)
 - rust-lang#130764 (Separate collection of crate-local inherent impls from error tracking)
 - rust-lang#130788 (Pin memchr to 2.5.0 in the library rather than rustc_ast)
 - rust-lang#130789 (add InProgress ErrorKind gated behind io_error_inprogress feature)
 - rust-lang#130793 (Mention `COMPILETEST_VERBOSE_CRASHES` on crash test failure)
 - rust-lang#130798 (rustdoc: inherit parent's stability where applicable)

Failed merges:

 - rust-lang#130735 (Simple validation for unsize coercion in MIR validation)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 75296fc into rust-lang:master Sep 25, 2024
6 checks passed
@rustbot rustbot added this to the 1.83.0 milestone Sep 25, 2024
@bors
Copy link
Contributor

bors commented Sep 25, 2024

⌛ Testing commit b62e72c with merge 1b5aa96...

rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Sep 25, 2024
Rollup merge of rust-lang#130234 - lukas-code:ptr-cast-errors, r=WaffleLapkin

improve compile errors for invalid ptr-to-ptr casts with trait objects

This is a follow-up to rust-lang#120248 to improve some of its error messages.

1. Make the borrowcheck error for "type annotation requires that x must outlive y" actually point at the type annotation, i.e. the type `T` in a `x as T` cast. This makes the error more consistent with other errors caused by type annotation in other places, such as
```text
error: lifetime may not live long enough
 --> src/lib.rs:4:12
  |
3 | fn bar(a: &i32) {
  |           - let's call the lifetime of this reference `'1`
4 |     let b: &'static i32 = a;
  |            ^^^^^^^^^^^^ type annotation requires that `'1` must outlive `'static`
```

2. Don't say "cast" when we actually mean "coercion" and give borrowcheck errors from actual casts (which is currently just the check added in rust-lang#120248) a higher priority than ones from coercions. This can improve the errors for ptr-to-ptr cast between trait objects because they are are lowered as an upcast "unsizing" coercion if possible (which may be the identity upcast) followed by the actual cast.

3. Bring back the old "casting X as Y is invalid" message for type mismatch in the principals and reword the "vtable kinds may not match" message to more accurately describe the pointer metadata and not refer to "vtables" if the metadata is unknown.

fixes rust-lang#130030

r? `@WaffleLapkin` but feel free to reassign
@lukas-code lukas-code deleted the ptr-cast-errors branch September 25, 2024 11:43
tautschnig added a commit to tautschnig/kani that referenced this pull request Sep 26, 2024
Changes required due to:
- rust-lang/rust#130234 improve compile errors for invalid ptr-to-ptr casts with trait objects
- rust-lang/rust@cfb8419900 Separate collection of crate-local inherent impls from error reporting
- rust-lang/rust@40fca8f7a8 Bump Clippy version -> 0.1.83

Resolves: model-checking#3548
github-merge-queue bot pushed a commit to model-checking/kani that referenced this pull request Sep 27, 2024
Changes required due to:
- rust-lang/rust#130234 improve compile errors
for invalid ptr-to-ptr casts with trait objects
- rust-lang/rust@cfb8419900 Separate collection of crate-local inherent
impls from error reporting
- rust-lang/rust@40fca8f7a8 Bump Clippy version -> 0.1.83

Resolves: #3548

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 and MIT licenses.
Zalathar added a commit to Zalathar/rust that referenced this pull request Oct 24, 2024
…piler-errors

minor `*dyn` cast cleanup

Small follow-up to rust-lang#130234 to remove a redundant check and clean up comments. No functional changes.

Also, explain why casts cannot drop the principal even though coercions can, and add a test because apparently we didn't have one already.

r? `@WaffleLapkin` or `@compiler-errors`
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Oct 24, 2024
Rollup merge of rust-lang#131898 - lukas-code:ptr-cast-cleanup, r=compiler-errors

minor `*dyn` cast cleanup

Small follow-up to rust-lang#130234 to remove a redundant check and clean up comments. No functional changes.

Also, explain why casts cannot drop the principal even though coercions can, and add a test because apparently we didn't have one already.

r? `@WaffleLapkin` or `@compiler-errors`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Confusing diagnostic for invalid casts of pointers to trait objects
8 participants