diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 2eea65125b038..3b98f358b1e95 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -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()) } diff --git a/tests/ui/dyn-star/pointer-like-impl-rules.rs b/tests/ui/dyn-star/pointer-like-impl-rules.rs new file mode 100644 index 0000000000000..c234e86e09a84 --- /dev/null +++ b/tests/ui/dyn-star/pointer-like-impl-rules.rs @@ -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); +impl PointerLike for GenericFieldIsNotPl {} +//~^ ERROR: implementation must be applied to type that +//~| NOTE: the field `0` of struct `GenericFieldIsNotPl` does not implement `PointerLike` + +#[repr(transparent)] +struct GenericFieldIsPl(T); +impl PointerLike for GenericFieldIsPl {} + +#[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 {} +//~^ ERROR: conflicting implementations of trait `PointerLike` +//~| NOTE: conflicting implementation in crate `alloc` + +impl PointerLike for Box {} +//~^ 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)); +} diff --git a/tests/ui/dyn-star/pointer-like-impl-rules.stderr b/tests/ui/dyn-star/pointer-like-impl-rules.stderr new file mode 100644 index 0000000000000..39f08f442c443 --- /dev/null +++ b/tests/ui/dyn-star/pointer-like-impl-rules.stderr @@ -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 PointerLike for &T; + +error[E0119]: conflicting implementations of trait `PointerLike` for type `Box` + --> $DIR/pointer-like-impl-rules.rs:68:1 + | +LL | impl PointerLike for Box {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `alloc`: + - impl PointerLike for Box; + +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 PointerLike for GenericFieldIsNotPl {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the field `0` of struct `GenericFieldIsNotPl` 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 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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`.