about summary refs log tree commit diff
path: root/compiler/rustc_hir_analysis/src/coherence/builtin.rs
diff options
context:
space:
mode:
authorKevin Reid <kpreid@switchb.org>2024-12-20 17:18:33 -0800
committerKevin Reid <kpreid@switchb.org>2024-12-20 20:49:09 -0800
commit7b500d852d449a4ef06680e89730a46159267d64 (patch)
treeab8982e42a4a6ee2ea10446d138fac3b8dd398c2 /compiler/rustc_hir_analysis/src/coherence/builtin.rs
parent13170cd787cb733ed24842ee825bcbd98dc01476 (diff)
downloadrust-7b500d852d449a4ef06680e89730a46159267d64.tar.gz
rust-7b500d852d449a4ef06680e89730a46159267d64.zip
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.
Diffstat (limited to 'compiler/rustc_hir_analysis/src/coherence/builtin.rs')
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs100
1 files changed, 69 insertions, 31 deletions
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 2eea65125b0..3b98f358b1e 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())
 }