Skip to content

Explain why a type is not eligible for impl PointerLike. #134603

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

Merged
merged 1 commit into from
Dec 22, 2024
Merged
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
100 changes: 69 additions & 31 deletions compiler/rustc_hir_analysis/src/coherence/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -717,12 +686,81 @@ 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(
impl_span,
"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 changes: 82 additions & 0 deletions tests/ui/dyn-star/pointer-like-impl-rules.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 85 additions & 0 deletions tests/ui/dyn-star/pointer-like-impl-rules.stderr
Original file line number Diff line number Diff line change
@@ -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