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:
Kevin Reid 2024-12-20 17:18:33 -08:00
parent 13170cd787
commit 7b500d852d
3 changed files with 236 additions and 31 deletions

View file

@ -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())
}