Explain why a type is not eligible for impl PointerLike
.
The rules were baffling when I ran in to them trying to add some impls, so I made the compiler explain them to me. The logic of the successful cases is unchanged, but I did rearrange it to reverse the order of the primitive and `Adt` cases; this makes producing the errors easier.
This commit is contained in:
parent
13170cd787
commit
7b500d852d
3 changed files with 236 additions and 31 deletions
|
@ -673,37 +673,6 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
|
|||
let impl_span = tcx.def_span(checker.impl_def_id);
|
||||
let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty();
|
||||
|
||||
// If an ADT is repr(transparent)...
|
||||
if let ty::Adt(def, args) = *self_ty.kind()
|
||||
&& def.repr().transparent()
|
||||
{
|
||||
// FIXME(compiler-errors): This should and could be deduplicated into a query.
|
||||
// Find the nontrivial field.
|
||||
let adt_typing_env = ty::TypingEnv::non_body_analysis(tcx, def.did());
|
||||
let nontrivial_field = def.all_fields().find(|field_def| {
|
||||
let field_ty = tcx.type_of(field_def.did).instantiate_identity();
|
||||
!tcx.layout_of(adt_typing_env.as_query_input(field_ty))
|
||||
.is_ok_and(|layout| layout.layout.is_1zst())
|
||||
});
|
||||
|
||||
if let Some(nontrivial_field) = nontrivial_field {
|
||||
// Check that the nontrivial field implements `PointerLike`.
|
||||
let nontrivial_field = nontrivial_field.ty(tcx, args);
|
||||
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
ocx.register_bound(
|
||||
ObligationCause::misc(impl_span, checker.impl_def_id),
|
||||
param_env,
|
||||
nontrivial_field,
|
||||
tcx.lang_items().pointer_like().unwrap(),
|
||||
);
|
||||
// FIXME(dyn-star): We should regionck this implementation.
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let is_permitted_primitive = match *self_ty.kind() {
|
||||
ty::Adt(def, _) => def.is_box(),
|
||||
ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
|
||||
|
@ -717,6 +686,74 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let why_disqualified = match *self_ty.kind() {
|
||||
// If an ADT is repr(transparent)
|
||||
ty::Adt(self_ty_def, args) => {
|
||||
if self_ty_def.repr().transparent() {
|
||||
// FIXME(compiler-errors): This should and could be deduplicated into a query.
|
||||
// Find the nontrivial field.
|
||||
let adt_typing_env = ty::TypingEnv::non_body_analysis(tcx, self_ty_def.did());
|
||||
let nontrivial_field = self_ty_def.all_fields().find(|field_def| {
|
||||
let field_ty = tcx.type_of(field_def.did).instantiate_identity();
|
||||
!tcx.layout_of(adt_typing_env.as_query_input(field_ty))
|
||||
.is_ok_and(|layout| layout.layout.is_1zst())
|
||||
});
|
||||
|
||||
if let Some(nontrivial_field) = nontrivial_field {
|
||||
// Check that the nontrivial field implements `PointerLike`.
|
||||
let nontrivial_field_ty = nontrivial_field.ty(tcx, args);
|
||||
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
ocx.register_bound(
|
||||
ObligationCause::misc(impl_span, checker.impl_def_id),
|
||||
param_env,
|
||||
nontrivial_field_ty,
|
||||
tcx.lang_items().pointer_like().unwrap(),
|
||||
);
|
||||
// FIXME(dyn-star): We should regionck this implementation.
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
return Ok(());
|
||||
} else {
|
||||
format!(
|
||||
"the field `{field_name}` of {descr} `{self_ty}` \
|
||||
does not implement `PointerLike`",
|
||||
field_name = nontrivial_field.name,
|
||||
descr = self_ty_def.descr()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
format!(
|
||||
"the {descr} `{self_ty}` is `repr(transparent)`, \
|
||||
but does not have a non-trivial field (it is zero-sized)",
|
||||
descr = self_ty_def.descr()
|
||||
)
|
||||
}
|
||||
} else if self_ty_def.is_box() {
|
||||
// If we got here, then the `layout.is_pointer_like()` check failed
|
||||
// and this box is not a thin pointer.
|
||||
|
||||
String::from("boxes of dynamically-sized types are too large to be `PointerLike`")
|
||||
} else {
|
||||
format!(
|
||||
"the {descr} `{self_ty}` is not `repr(transparent)`",
|
||||
descr = self_ty_def.descr()
|
||||
)
|
||||
}
|
||||
}
|
||||
ty::Ref(..) => {
|
||||
// If we got here, then the `layout.is_pointer_like()` check failed
|
||||
// and this reference is not a thin pointer.
|
||||
String::from("references to dynamically-sized types are too large to be `PointerLike`")
|
||||
}
|
||||
ty::Dynamic(..) | ty::Foreign(..) => {
|
||||
String::from("types of dynamic or unknown size may not implement `PointerLike`")
|
||||
}
|
||||
_ => {
|
||||
// This is a white lie; it is true everywhere outside the standard library.
|
||||
format!("only user-defined sized types are eligible for `impl PointerLike`")
|
||||
}
|
||||
};
|
||||
|
||||
Err(tcx
|
||||
.dcx()
|
||||
.struct_span_err(
|
||||
|
@ -724,5 +761,6 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
|
|||
"implementation must be applied to type that has the same ABI as a pointer, \
|
||||
or is `repr(transparent)` and whose field is `PointerLike`",
|
||||
)
|
||||
.with_note(why_disqualified)
|
||||
.emit())
|
||||
}
|
||||
|
|
82
tests/ui/dyn-star/pointer-like-impl-rules.rs
Normal file
82
tests/ui/dyn-star/pointer-like-impl-rules.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
//@ check-fail
|
||||
|
||||
#![feature(extern_types)]
|
||||
#![feature(pointer_like_trait)]
|
||||
|
||||
use std::marker::PointerLike;
|
||||
|
||||
struct NotReprTransparent;
|
||||
impl PointerLike for NotReprTransparent {}
|
||||
//~^ ERROR: implementation must be applied to type that
|
||||
//~| NOTE: the struct `NotReprTransparent` is not `repr(transparent)`
|
||||
|
||||
#[repr(transparent)]
|
||||
struct FieldIsPl(usize);
|
||||
impl PointerLike for FieldIsPl {}
|
||||
|
||||
#[repr(transparent)]
|
||||
struct FieldIsPlAndHasOtherField(usize, ());
|
||||
impl PointerLike for FieldIsPlAndHasOtherField {}
|
||||
|
||||
#[repr(transparent)]
|
||||
struct FieldIsNotPl(u8);
|
||||
impl PointerLike for FieldIsNotPl {}
|
||||
//~^ ERROR: implementation must be applied to type that
|
||||
//~| NOTE: the field `0` of struct `FieldIsNotPl` does not implement `PointerLike`
|
||||
|
||||
#[repr(transparent)]
|
||||
struct GenericFieldIsNotPl<T>(T);
|
||||
impl<T> PointerLike for GenericFieldIsNotPl<T> {}
|
||||
//~^ ERROR: implementation must be applied to type that
|
||||
//~| NOTE: the field `0` of struct `GenericFieldIsNotPl<T>` does not implement `PointerLike`
|
||||
|
||||
#[repr(transparent)]
|
||||
struct GenericFieldIsPl<T>(T);
|
||||
impl<T: PointerLike> PointerLike for GenericFieldIsPl<T> {}
|
||||
|
||||
#[repr(transparent)]
|
||||
struct IsZeroSized(());
|
||||
impl PointerLike for IsZeroSized {}
|
||||
//~^ ERROR: implementation must be applied to type that
|
||||
//~| NOTE: the struct `IsZeroSized` is `repr(transparent)`, but does not have a non-trivial field
|
||||
|
||||
trait SomeTrait {}
|
||||
impl PointerLike for dyn SomeTrait {}
|
||||
//~^ ERROR: implementation must be applied to type that
|
||||
//~| NOTE: types of dynamic or unknown size
|
||||
|
||||
extern "C" {
|
||||
type ExternType;
|
||||
}
|
||||
impl PointerLike for ExternType {}
|
||||
//~^ ERROR: implementation must be applied to type that
|
||||
//~| NOTE: types of dynamic or unknown size
|
||||
|
||||
struct LocalSizedType(&'static str);
|
||||
struct LocalUnsizedType(str);
|
||||
|
||||
// This is not a special error but a normal coherence error,
|
||||
// which should still happen.
|
||||
impl PointerLike for &LocalSizedType {}
|
||||
//~^ ERROR: conflicting implementations of trait `PointerLike`
|
||||
//~| NOTE: conflicting implementation in crate `core`
|
||||
|
||||
impl PointerLike for &LocalUnsizedType {}
|
||||
//~^ ERROR: implementation must be applied to type that
|
||||
//~| NOTE: references to dynamically-sized types are too large to be `PointerLike`
|
||||
|
||||
impl PointerLike for Box<LocalSizedType> {}
|
||||
//~^ ERROR: conflicting implementations of trait `PointerLike`
|
||||
//~| NOTE: conflicting implementation in crate `alloc`
|
||||
|
||||
impl PointerLike for Box<LocalUnsizedType> {}
|
||||
//~^ ERROR: implementation must be applied to type that
|
||||
//~| NOTE: boxes of dynamically-sized types are too large to be `PointerLike`
|
||||
|
||||
fn expects_pointer_like(x: impl PointerLike) {}
|
||||
|
||||
fn main() {
|
||||
expects_pointer_like(FieldIsPl(1usize));
|
||||
expects_pointer_like(FieldIsPlAndHasOtherField(1usize, ()));
|
||||
expects_pointer_like(GenericFieldIsPl(1usize));
|
||||
}
|
85
tests/ui/dyn-star/pointer-like-impl-rules.stderr
Normal file
85
tests/ui/dyn-star/pointer-like-impl-rules.stderr
Normal file
|
@ -0,0 +1,85 @@
|
|||
error[E0119]: conflicting implementations of trait `PointerLike` for type `&LocalSizedType`
|
||||
--> $DIR/pointer-like-impl-rules.rs:60:1
|
||||
|
|
||||
LL | impl PointerLike for &LocalSizedType {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: conflicting implementation in crate `core`:
|
||||
- impl<T> PointerLike for &T;
|
||||
|
||||
error[E0119]: conflicting implementations of trait `PointerLike` for type `Box<LocalSizedType>`
|
||||
--> $DIR/pointer-like-impl-rules.rs:68:1
|
||||
|
|
||||
LL | impl PointerLike for Box<LocalSizedType> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: conflicting implementation in crate `alloc`:
|
||||
- impl<T> PointerLike for Box<T>;
|
||||
|
||||
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
|
||||
--> $DIR/pointer-like-impl-rules.rs:9:1
|
||||
|
|
||||
LL | impl PointerLike for NotReprTransparent {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the struct `NotReprTransparent` is not `repr(transparent)`
|
||||
|
||||
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
|
||||
--> $DIR/pointer-like-impl-rules.rs:23:1
|
||||
|
|
||||
LL | impl PointerLike for FieldIsNotPl {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the field `0` of struct `FieldIsNotPl` does not implement `PointerLike`
|
||||
|
||||
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
|
||||
--> $DIR/pointer-like-impl-rules.rs:29:1
|
||||
|
|
||||
LL | impl<T> PointerLike for GenericFieldIsNotPl<T> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the field `0` of struct `GenericFieldIsNotPl<T>` does not implement `PointerLike`
|
||||
|
||||
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
|
||||
--> $DIR/pointer-like-impl-rules.rs:39:1
|
||||
|
|
||||
LL | impl PointerLike for IsZeroSized {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the struct `IsZeroSized` is `repr(transparent)`, but does not have a non-trivial field (it is zero-sized)
|
||||
|
||||
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
|
||||
--> $DIR/pointer-like-impl-rules.rs:44:1
|
||||
|
|
||||
LL | impl PointerLike for dyn SomeTrait {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: types of dynamic or unknown size may not implement `PointerLike`
|
||||
|
||||
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
|
||||
--> $DIR/pointer-like-impl-rules.rs:51:1
|
||||
|
|
||||
LL | impl PointerLike for ExternType {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: types of dynamic or unknown size may not implement `PointerLike`
|
||||
|
||||
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
|
||||
--> $DIR/pointer-like-impl-rules.rs:64:1
|
||||
|
|
||||
LL | impl PointerLike for &LocalUnsizedType {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: references to dynamically-sized types are too large to be `PointerLike`
|
||||
|
||||
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
|
||||
--> $DIR/pointer-like-impl-rules.rs:72:1
|
||||
|
|
||||
LL | impl PointerLike for Box<LocalUnsizedType> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: boxes of dynamically-sized types are too large to be `PointerLike`
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0119`.
|
Loading…
Add table
Add a link
Reference in a new issue