Skip to content

Commit 7cf9156

Browse files
authored
Rollup merge of rust-lang#134603 - kpreid:pointerlike-err, r=estebank
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 (to `std`, not my own code, as it happens), 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. I'm still not very familiar with `rustc` internals, so let me know if there's a better way to do any of this. This also adds test coverage for which impls are accepted or rejected, which I didn't see any of already. The PR template tells me I should consider mentioning a tracking issue, but there isn't one for `pointer_like_trait`, so I'll mention `dyn_star`: rust-lang#102425
2 parents bcdde4e + 7b500d8 commit 7cf9156

File tree

3 files changed

+236
-31
lines changed

3 files changed

+236
-31
lines changed

compiler/rustc_hir_analysis/src/coherence/builtin.rs

+69-31
Original file line numberDiff line numberDiff line change
@@ -673,37 +673,6 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
673673
let impl_span = tcx.def_span(checker.impl_def_id);
674674
let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty();
675675

676-
// If an ADT is repr(transparent)...
677-
if let ty::Adt(def, args) = *self_ty.kind()
678-
&& def.repr().transparent()
679-
{
680-
// FIXME(compiler-errors): This should and could be deduplicated into a query.
681-
// Find the nontrivial field.
682-
let adt_typing_env = ty::TypingEnv::non_body_analysis(tcx, def.did());
683-
let nontrivial_field = def.all_fields().find(|field_def| {
684-
let field_ty = tcx.type_of(field_def.did).instantiate_identity();
685-
!tcx.layout_of(adt_typing_env.as_query_input(field_ty))
686-
.is_ok_and(|layout| layout.layout.is_1zst())
687-
});
688-
689-
if let Some(nontrivial_field) = nontrivial_field {
690-
// Check that the nontrivial field implements `PointerLike`.
691-
let nontrivial_field = nontrivial_field.ty(tcx, args);
692-
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
693-
let ocx = ObligationCtxt::new(&infcx);
694-
ocx.register_bound(
695-
ObligationCause::misc(impl_span, checker.impl_def_id),
696-
param_env,
697-
nontrivial_field,
698-
tcx.lang_items().pointer_like().unwrap(),
699-
);
700-
// FIXME(dyn-star): We should regionck this implementation.
701-
if ocx.select_all_or_error().is_empty() {
702-
return Ok(());
703-
}
704-
}
705-
}
706-
707676
let is_permitted_primitive = match *self_ty.kind() {
708677
ty::Adt(def, _) => def.is_box(),
709678
ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
@@ -717,12 +686,81 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
717686
return Ok(());
718687
}
719688

689+
let why_disqualified = match *self_ty.kind() {
690+
// If an ADT is repr(transparent)
691+
ty::Adt(self_ty_def, args) => {
692+
if self_ty_def.repr().transparent() {
693+
// FIXME(compiler-errors): This should and could be deduplicated into a query.
694+
// Find the nontrivial field.
695+
let adt_typing_env = ty::TypingEnv::non_body_analysis(tcx, self_ty_def.did());
696+
let nontrivial_field = self_ty_def.all_fields().find(|field_def| {
697+
let field_ty = tcx.type_of(field_def.did).instantiate_identity();
698+
!tcx.layout_of(adt_typing_env.as_query_input(field_ty))
699+
.is_ok_and(|layout| layout.layout.is_1zst())
700+
});
701+
702+
if let Some(nontrivial_field) = nontrivial_field {
703+
// Check that the nontrivial field implements `PointerLike`.
704+
let nontrivial_field_ty = nontrivial_field.ty(tcx, args);
705+
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
706+
let ocx = ObligationCtxt::new(&infcx);
707+
ocx.register_bound(
708+
ObligationCause::misc(impl_span, checker.impl_def_id),
709+
param_env,
710+
nontrivial_field_ty,
711+
tcx.lang_items().pointer_like().unwrap(),
712+
);
713+
// FIXME(dyn-star): We should regionck this implementation.
714+
if ocx.select_all_or_error().is_empty() {
715+
return Ok(());
716+
} else {
717+
format!(
718+
"the field `{field_name}` of {descr} `{self_ty}` \
719+
does not implement `PointerLike`",
720+
field_name = nontrivial_field.name,
721+
descr = self_ty_def.descr()
722+
)
723+
}
724+
} else {
725+
format!(
726+
"the {descr} `{self_ty}` is `repr(transparent)`, \
727+
but does not have a non-trivial field (it is zero-sized)",
728+
descr = self_ty_def.descr()
729+
)
730+
}
731+
} else if self_ty_def.is_box() {
732+
// If we got here, then the `layout.is_pointer_like()` check failed
733+
// and this box is not a thin pointer.
734+
735+
String::from("boxes of dynamically-sized types are too large to be `PointerLike`")
736+
} else {
737+
format!(
738+
"the {descr} `{self_ty}` is not `repr(transparent)`",
739+
descr = self_ty_def.descr()
740+
)
741+
}
742+
}
743+
ty::Ref(..) => {
744+
// If we got here, then the `layout.is_pointer_like()` check failed
745+
// and this reference is not a thin pointer.
746+
String::from("references to dynamically-sized types are too large to be `PointerLike`")
747+
}
748+
ty::Dynamic(..) | ty::Foreign(..) => {
749+
String::from("types of dynamic or unknown size may not implement `PointerLike`")
750+
}
751+
_ => {
752+
// This is a white lie; it is true everywhere outside the standard library.
753+
format!("only user-defined sized types are eligible for `impl PointerLike`")
754+
}
755+
};
756+
720757
Err(tcx
721758
.dcx()
722759
.struct_span_err(
723760
impl_span,
724761
"implementation must be applied to type that has the same ABI as a pointer, \
725762
or is `repr(transparent)` and whose field is `PointerLike`",
726763
)
764+
.with_note(why_disqualified)
727765
.emit())
728766
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//@ check-fail
2+
3+
#![feature(extern_types)]
4+
#![feature(pointer_like_trait)]
5+
6+
use std::marker::PointerLike;
7+
8+
struct NotReprTransparent;
9+
impl PointerLike for NotReprTransparent {}
10+
//~^ ERROR: implementation must be applied to type that
11+
//~| NOTE: the struct `NotReprTransparent` is not `repr(transparent)`
12+
13+
#[repr(transparent)]
14+
struct FieldIsPl(usize);
15+
impl PointerLike for FieldIsPl {}
16+
17+
#[repr(transparent)]
18+
struct FieldIsPlAndHasOtherField(usize, ());
19+
impl PointerLike for FieldIsPlAndHasOtherField {}
20+
21+
#[repr(transparent)]
22+
struct FieldIsNotPl(u8);
23+
impl PointerLike for FieldIsNotPl {}
24+
//~^ ERROR: implementation must be applied to type that
25+
//~| NOTE: the field `0` of struct `FieldIsNotPl` does not implement `PointerLike`
26+
27+
#[repr(transparent)]
28+
struct GenericFieldIsNotPl<T>(T);
29+
impl<T> PointerLike for GenericFieldIsNotPl<T> {}
30+
//~^ ERROR: implementation must be applied to type that
31+
//~| NOTE: the field `0` of struct `GenericFieldIsNotPl<T>` does not implement `PointerLike`
32+
33+
#[repr(transparent)]
34+
struct GenericFieldIsPl<T>(T);
35+
impl<T: PointerLike> PointerLike for GenericFieldIsPl<T> {}
36+
37+
#[repr(transparent)]
38+
struct IsZeroSized(());
39+
impl PointerLike for IsZeroSized {}
40+
//~^ ERROR: implementation must be applied to type that
41+
//~| NOTE: the struct `IsZeroSized` is `repr(transparent)`, but does not have a non-trivial field
42+
43+
trait SomeTrait {}
44+
impl PointerLike for dyn SomeTrait {}
45+
//~^ ERROR: implementation must be applied to type that
46+
//~| NOTE: types of dynamic or unknown size
47+
48+
extern "C" {
49+
type ExternType;
50+
}
51+
impl PointerLike for ExternType {}
52+
//~^ ERROR: implementation must be applied to type that
53+
//~| NOTE: types of dynamic or unknown size
54+
55+
struct LocalSizedType(&'static str);
56+
struct LocalUnsizedType(str);
57+
58+
// This is not a special error but a normal coherence error,
59+
// which should still happen.
60+
impl PointerLike for &LocalSizedType {}
61+
//~^ ERROR: conflicting implementations of trait `PointerLike`
62+
//~| NOTE: conflicting implementation in crate `core`
63+
64+
impl PointerLike for &LocalUnsizedType {}
65+
//~^ ERROR: implementation must be applied to type that
66+
//~| NOTE: references to dynamically-sized types are too large to be `PointerLike`
67+
68+
impl PointerLike for Box<LocalSizedType> {}
69+
//~^ ERROR: conflicting implementations of trait `PointerLike`
70+
//~| NOTE: conflicting implementation in crate `alloc`
71+
72+
impl PointerLike for Box<LocalUnsizedType> {}
73+
//~^ ERROR: implementation must be applied to type that
74+
//~| NOTE: boxes of dynamically-sized types are too large to be `PointerLike`
75+
76+
fn expects_pointer_like(x: impl PointerLike) {}
77+
78+
fn main() {
79+
expects_pointer_like(FieldIsPl(1usize));
80+
expects_pointer_like(FieldIsPlAndHasOtherField(1usize, ()));
81+
expects_pointer_like(GenericFieldIsPl(1usize));
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
error[E0119]: conflicting implementations of trait `PointerLike` for type `&LocalSizedType`
2+
--> $DIR/pointer-like-impl-rules.rs:60:1
3+
|
4+
LL | impl PointerLike for &LocalSizedType {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: conflicting implementation in crate `core`:
8+
- impl<T> PointerLike for &T;
9+
10+
error[E0119]: conflicting implementations of trait `PointerLike` for type `Box<LocalSizedType>`
11+
--> $DIR/pointer-like-impl-rules.rs:68:1
12+
|
13+
LL | impl PointerLike for Box<LocalSizedType> {}
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
|
16+
= note: conflicting implementation in crate `alloc`:
17+
- impl<T> PointerLike for Box<T>;
18+
19+
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
20+
--> $DIR/pointer-like-impl-rules.rs:9:1
21+
|
22+
LL | impl PointerLike for NotReprTransparent {}
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
|
25+
= note: the struct `NotReprTransparent` is not `repr(transparent)`
26+
27+
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
28+
--> $DIR/pointer-like-impl-rules.rs:23:1
29+
|
30+
LL | impl PointerLike for FieldIsNotPl {}
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
|
33+
= note: the field `0` of struct `FieldIsNotPl` does not implement `PointerLike`
34+
35+
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
36+
--> $DIR/pointer-like-impl-rules.rs:29:1
37+
|
38+
LL | impl<T> PointerLike for GenericFieldIsNotPl<T> {}
39+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40+
|
41+
= note: the field `0` of struct `GenericFieldIsNotPl<T>` does not implement `PointerLike`
42+
43+
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
44+
--> $DIR/pointer-like-impl-rules.rs:39:1
45+
|
46+
LL | impl PointerLike for IsZeroSized {}
47+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
48+
|
49+
= note: the struct `IsZeroSized` is `repr(transparent)`, but does not have a non-trivial field (it is zero-sized)
50+
51+
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
52+
--> $DIR/pointer-like-impl-rules.rs:44:1
53+
|
54+
LL | impl PointerLike for dyn SomeTrait {}
55+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56+
|
57+
= note: types of dynamic or unknown size may not implement `PointerLike`
58+
59+
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
60+
--> $DIR/pointer-like-impl-rules.rs:51:1
61+
|
62+
LL | impl PointerLike for ExternType {}
63+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
64+
|
65+
= note: types of dynamic or unknown size may not implement `PointerLike`
66+
67+
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
68+
--> $DIR/pointer-like-impl-rules.rs:64:1
69+
|
70+
LL | impl PointerLike for &LocalUnsizedType {}
71+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
72+
|
73+
= note: references to dynamically-sized types are too large to be `PointerLike`
74+
75+
error: implementation must be applied to type that has the same ABI as a pointer, or is `repr(transparent)` and whose field is `PointerLike`
76+
--> $DIR/pointer-like-impl-rules.rs:72:1
77+
|
78+
LL | impl PointerLike for Box<LocalUnsizedType> {}
79+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
80+
|
81+
= note: boxes of dynamically-sized types are too large to be `PointerLike`
82+
83+
error: aborting due to 10 previous errors
84+
85+
For more information about this error, try `rustc --explain E0119`.

0 commit comments

Comments
 (0)