Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
424 changes: 326 additions & 98 deletions compiler/rustc_hir_analysis/src/coherence/builtin.rs

Large diffs are not rendered by default.

64 changes: 63 additions & 1 deletion compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1371,13 +1371,75 @@ pub(crate) struct CoerceSharedNotSingleLifetimeParam {
}

#[derive(Diagnostic)]
#[diag("implementing `{$trait_name}` does not allow multiple lifetimes or fields to be coerced")]
#[diag(
"implementing `{$trait_name}` requires exactly one lifetime argument in the reborrowed type"
)]
pub(crate) struct CoerceSharedMulti {
#[primary_span]
pub span: Span,
pub trait_name: &'static str,
}

#[derive(Diagnostic)]
#[diag(
"implementing `{$trait_name}` requires source and target to use the same reborrow lifetime \
argument"
)]
pub(crate) struct CoerceSharedLifetimeMismatch {
#[primary_span]
pub span: Span,
pub trait_name: &'static str,
}

#[derive(Diagnostic)]
#[diag(
"implementing `{$trait_name}` requires corresponding fields to match, \
be reborrowable with `CoerceShared`, or coerce a mutable reference field \
to a shared reference field"
)]
pub(crate) struct CoerceSharedFieldMismatch<'tcx> {
#[primary_span]
#[label("target field `{$target_name}` has type `{$target_ty}`")]
pub span: Span,
#[label("source field `{$source_name}` has type `{$source_ty}`")]
pub source_span: Span,
pub source_name: Symbol,
pub source_ty: Ty<'tcx>,
pub target_name: Symbol,
pub target_ty: Ty<'tcx>,
pub trait_name: &'static str,
}

#[derive(Diagnostic)]
#[diag(
"implementing `{$trait_name}` requires every target field to have a corresponding source field"
)]
pub(crate) struct CoerceSharedMissingField {
#[primary_span]
pub span: Span,
pub trait_name: &'static str,
}

#[derive(Diagnostic)]
#[diag(
"implementing `{$trait_name}` requires source and target structs to use the same field style"
)]
pub(crate) struct CoerceSharedFieldStyleMismatch {
#[primary_span]
pub span: Span,
pub trait_name: &'static str,
}

#[derive(Diagnostic)]
#[diag(
"implementing `{$trait_name}` requires all source and target fields to be accessible from the impl"
)]
pub(crate) struct CoerceSharedInaccessibleField {
#[primary_span]
pub span: Span,
pub trait_name: &'static str,
}

#[derive(Diagnostic)]
#[diag("the trait `{$trait_name}` may only be implemented for a coercion between structures", code = E0377)]
pub(crate) struct CoerceUnsizedNonStruct {
Expand Down
22 changes: 22 additions & 0 deletions tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#![allow(dead_code)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thought: I believe you could move the ForeignPtrRef into a mod foreign_ptr {} block inside the actual test file.


use std::marker::PhantomData;

#[derive(Clone, Copy)]
pub struct ForeignRef<'a> {
value: &'a i32,
}

// SAFETY invariant: the pointer is valid as `&'a i32`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue: Comment unrelated to test code.

#[derive(Clone, Copy)]
pub struct ForeignPtrRef<'a>((*const i32, PhantomData<&'a ()>));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick: The two types are subtly different to one another for no particular reason.


impl<'a> ForeignPtrRef<'a> {
pub fn new(r: &'a i32) -> Self {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue: Whole implementation part unrelated to test code.

ForeignPtrRef((r, PhantomData))
}

pub fn to_ref(self) -> &'a i32 {
unsafe { &*self.0.0 }
}
}
31 changes: 31 additions & 0 deletions tests/ui/reborrow/coerce-shared-associated-type-field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//@ check-pass

#![feature(reborrow)]
#![allow(dead_code)]

use std::marker::{CoerceShared, Reborrow};

trait Trait {
type Assoc;
}

impl Trait for i32 {
type Assoc = i64;
}

struct MyMut<'a> {
x: &'a (),
y: i64,
}

#[derive(Copy, Clone)]
struct MyRef<'a> {
x: &'a (),
y: <i32 as Trait>::Assoc,
}

impl Reborrow for MyMut<'_> {}

impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {}

fn main() {}
27 changes: 27 additions & 0 deletions tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//@ check-pass

#![feature(reborrow, decl_macro)]
#![allow(incomplete_features)]

use std::marker::{CoerceShared, Reborrow};

macro my_macro($field:ident) {
pub struct MyMut<'a> {
$field: &'a i32,
field: &'a i64,
}

#[derive(Clone, Copy)]
pub struct MyRef<'a> {
$field: &'a i32,
field: &'a i64,
}

impl Reborrow for MyMut<'_> {}

impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {}
}

my_macro!(field);

fn main() {}
23 changes: 23 additions & 0 deletions tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@ normalize-stderr: "\n\n\z" -> "\n"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick: This does not appear anywhere in the code base currently. Normally this normalisation is used to deal with various platform-dependent outputs and random sources. This sort of "let's make the stderr file a little smaller in size" seems like an unnecessary complication.


#![feature(reborrow)]

use std::marker::{CoerceShared, Reborrow};

struct MyMut<'a> {
x: &'static (),
y: &'a (),
}

impl Reborrow for MyMut<'_> {}

#[derive(Copy, Clone)]
struct MyRef<'a> {
x: &'a (),
//~^ ERROR
y: &'static (),
}

impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {}

fn main() {}
10 changes: 10 additions & 0 deletions tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field
--> $DIR/coerce-shared-field-lifetime-swap.rs:16:5
|
LL | x: &'static (),
| -------------- source field `x` has type `&'static ()`
...
LL | x: &'a (),
| ^^^^^^^^^ target field `x` has type `&'a ()`

error: aborting due to 1 previous error
54 changes: 54 additions & 0 deletions tests/ui/reborrow/coerce-shared-field-relations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//@ normalize-stderr: "\n\n\z" -> "\n"

#![feature(reborrow)]

use std::marker::{CoerceShared, Reborrow};

struct CustomMut<'a, T> {
value: &'a mut T,
}

impl<'a, T> Reborrow for CustomMut<'a, T> {}

#[derive(Clone, Copy)]
struct CustomRef<'a, T> {
value: &'a T,
}

impl<'a, T> CoerceShared<CustomRef<'a, T>> for CustomMut<'a, T> {}

struct RenamedMut<'a, T> {
source: &'a mut T,
}

impl<'a, T> Reborrow for RenamedMut<'a, T> {}

#[derive(Clone, Copy)]
struct RenamedRef<'a, T> {
target: &'a T,
//~^ ERROR
}

impl<'a, T> CoerceShared<RenamedRef<'a, T>> for RenamedMut<'a, T> {}

struct BadMut<'a, T> {
value: &'a mut T,
}

impl<'a, T> Reborrow for BadMut<'a, T> {}

#[derive(Clone, Copy)]
struct BadRef<'a, T> {
value: &'a u32,
//~^ ERROR
_marker: std::marker::PhantomData<T>,
}

impl<'a, T> CoerceShared<BadRef<'a, T>> for BadMut<'a, T> {}

fn good(_value: CustomRef<'_, u32>) {}

fn main() {
let mut value = 1;
good(CustomMut { value: &mut value });
}
16 changes: 16 additions & 0 deletions tests/ui/reborrow/coerce-shared-field-relations.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error: implementing `CoerceShared` requires every target field to have a corresponding source field
--> $DIR/coerce-shared-field-relations.rs:28:5
|
LL | target: &'a T,
| ^^^^^^^^^^^^^

error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field
--> $DIR/coerce-shared-field-relations.rs:42:5
|
LL | value: &'a mut T,
| ---------------- source field `value` has type `&'a mut T`
...
LL | value: &'a u32,
| ^^^^^^^^^^^^^^ target field `value` has type `&'a u32`

error: aborting due to 2 previous errors
21 changes: 21 additions & 0 deletions tests/ui/reborrow/coerce-shared-foreign-private-field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//@ normalize-stderr: "\n\n\z" -> "\n"

//@ aux-build: reborrow_foreign_private.rs

#![feature(reborrow)]

extern crate reborrow_foreign_private;

use reborrow_foreign_private::ForeignRef;
use std::marker::{CoerceShared, Reborrow};

struct LocalMut<'a> {
value: &'a mut i32,
}

impl<'a> Reborrow for LocalMut<'a> {}

impl<'a> CoerceShared<ForeignRef<'a>> for LocalMut<'a> {}
//~^ ERROR

fn main() {}
7 changes: 7 additions & 0 deletions tests/ui/reborrow/coerce-shared-foreign-private-field.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: implementing `CoerceShared` requires all source and target fields to be accessible from the impl
--> $DIR/coerce-shared-foreign-private-field.rs:18:1
|
LL | impl<'a> CoerceShared<ForeignRef<'a>> for LocalMut<'a> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error
24 changes: 24 additions & 0 deletions tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//@ normalize-stderr: "\n\n\z" -> "\n"

//@ aux-build: reborrow_foreign_private.rs

#![feature(reborrow)]

extern crate reborrow_foreign_private;

use reborrow_foreign_private::ForeignPtrRef;
use std::marker::{CoerceShared, PhantomData, Reborrow};
use std::ptr;

struct LocalPtrMut<'a>((*const i32, PhantomData<&'a ()>));

impl<'a> Reborrow for LocalPtrMut<'a> {}

impl<'a> CoerceShared<ForeignPtrRef<'a>> for LocalPtrMut<'a> {}
//~^ ERROR

fn main() {
let local = LocalPtrMut((ptr::null(), PhantomData));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue: This code here is not related to what is actually being tested here AFAIU. The LocalMut/ForeignRef test above does not have anything in main.

let foreign: ForeignPtrRef<'_> = local;
let _ = foreign.to_ref();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: implementing `CoerceShared` requires all source and target fields to be accessible from the impl
--> $DIR/coerce-shared-foreign-private-tuple-field.rs:17:1
|
LL | impl<'a> CoerceShared<ForeignPtrRef<'a>> for LocalPtrMut<'a> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error
21 changes: 21 additions & 0 deletions tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#![feature(reborrow)]

use std::marker::{CoerceShared, PhantomData, Reborrow};

struct CustomMarker<'a>(PhantomData<&'a ()>);

impl<'a> Reborrow for CustomMarker<'a> {}

#[derive(Clone, Copy)]
struct StaticMarkerRef<'a>(PhantomData<&'a ()>);

impl<'a> CoerceShared<StaticMarkerRef<'static>> for CustomMarker<'a> {}
//~^ ERROR

fn method(_a: StaticMarkerRef<'static>) {}

fn main() {
let a = CustomMarker(PhantomData);
method(a);
//~^ ERROR
}
23 changes: 23 additions & 0 deletions tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error: implementing `CoerceShared` requires source and target to use the same reborrow lifetime argument
--> $DIR/coerce-shared-lifetime-mismatch.rs:12:10
|
LL | impl<'a> CoerceShared<StaticMarkerRef<'static>> for CustomMarker<'a> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0597]: `a` does not live long enough
--> $DIR/coerce-shared-lifetime-mismatch.rs:19:12
|
LL | let a = CustomMarker(PhantomData);
| - binding `a` declared here
LL | method(a);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thought: Huh... this seems confusing to me: if we've given an error from the CoerceShared implementation then I'd have assumed that here we'd get an error that CustomMarker does not match type StaticMarkerRef, ie. no adjustment should happen because CustomMarker should not implement CoerceShared...

| -------^-
| | |
| | borrowed value does not live long enough
| argument requires that `a` is borrowed for `'static`
LL |
LL | }
| - `a` dropped here while still borrowed

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0597`.
22 changes: 22 additions & 0 deletions tests/ui/reborrow/coerce-shared-missing-target-field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//@ normalize-stderr: "\n\n\z" -> "\n"

#![feature(reborrow)]

use std::marker::{CoerceShared, Reborrow};

struct MissingSourceMut<'a, T> {
value: &'a mut T,
}

impl<'a, T> Reborrow for MissingSourceMut<'a, T> {}

#[derive(Clone, Copy)]
struct MissingSourceRef<'a, T> {
value: &'a T,
len: usize,
//~^ ERROR
}

impl<'a, T> CoerceShared<MissingSourceRef<'a, T>> for MissingSourceMut<'a, T> {}

fn main() {}
Loading
Loading