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 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();
|
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() {
|
let is_permitted_primitive = match *self_ty.kind() {
|
||||||
ty::Adt(def, _) => def.is_box(),
|
ty::Adt(def, _) => def.is_box(),
|
||||||
ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
|
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(());
|
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
|
Err(tcx
|
||||||
.dcx()
|
.dcx()
|
||||||
.struct_span_err(
|
.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, \
|
"implementation must be applied to type that has the same ABI as a pointer, \
|
||||||
or is `repr(transparent)` and whose field is `PointerLike`",
|
or is `repr(transparent)` and whose field is `PointerLike`",
|
||||||
)
|
)
|
||||||
|
.with_note(why_disqualified)
|
||||||
.emit())
|
.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