From b454f76bd1ddf5c7459238f295647561a7895cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 11 Jan 2026 17:45:50 +0100 Subject: [PATCH] ensure generics are still properly reported on EII *implementations*, and test this --- compiler/rustc_hir_analysis/messages.ftl | 5 +++ .../src/check/compare_eii.rs | 38 ++++++++++++++++++- compiler/rustc_hir_analysis/src/errors.rs | 12 ++++++ .../type_checking/generic_implementation.rs | 13 +++++++ .../generic_implementation.stderr | 12 ++++++ 5 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 tests/ui/eii/type_checking/generic_implementation.rs create mode 100644 tests/ui/eii/type_checking/generic_implementation.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 9ead1225d5f5..fa3c4cb05f96 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -165,6 +165,11 @@ hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice .label = parameter captured again here +hir_analysis_eii_with_generics = + `{$impl_name}` cannot have generic parameters other than lifetimes + .label = required by this attribute + .help = `#[{$eii_name}]` marks the implementation of an "externally implementable item" + hir_analysis_empty_specialization = specialization impl does not specialize any associated items .note = impl is a specialization of this impl diff --git a/compiler/rustc_hir_analysis/src/check/compare_eii.rs b/compiler/rustc_hir_analysis/src/check/compare_eii.rs index c2a9b1fbdee0..2beb7eb09c11 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_eii.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_eii.rs @@ -8,8 +8,9 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, E0806, struct_span_code_err}; +use rustc_hir::attrs::{AttributeKind, EiiImplResolution}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, FnSig, HirId, ItemKind}; +use rustc_hir::{self as hir, FnSig, HirId, ItemKind, find_attr}; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -24,7 +25,7 @@ use crate::check::compare_impl_item::{ CheckNumberOfEarlyBoundRegionsError, check_number_of_early_bound_regions, }; -use crate::errors::LifetimesOrBoundsMismatchOnEii; +use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii}; /// Checks whether the signature of some `external_impl`, matches /// the signature of `declaration`, which it is supposed to be compatible @@ -154,11 +155,44 @@ fn check_is_structurally_compatible<'tcx>( eii_name: Symbol, eii_attr_span: Span, ) -> Result<(), ErrorGuaranteed> { + check_no_generics(tcx, external_impl, declaration, eii_name, eii_attr_span)?; check_number_of_arguments(tcx, external_impl, declaration, eii_name, eii_attr_span)?; check_early_region_bounds(tcx, external_impl, declaration, eii_attr_span)?; Ok(()) } +/// externally implementable items can't have generics +fn check_no_generics<'tcx>( + tcx: TyCtxt<'tcx>, + external_impl: LocalDefId, + _declaration: DefId, + eii_name: Symbol, + eii_attr_span: Span, +) -> Result<(), ErrorGuaranteed> { + let generics = tcx.generics_of(external_impl); + if generics.own_requires_monomorphization() + // When an EII implementation is automatically generated by the `#[eii]` macro, + // it will directly refer to the foreign item, not through a macro. + // We don't want to emit this error if it's an implementation that's generated by the `#[eii]` macro, + // since in that case it looks like a duplicate error: the declaration of the EII already can't contain generics. + // So, we check here if at least one of the eii impls has ImplResolution::Macro, which indicates it's + // not generated as part of the declaration. + && find_attr!( + tcx.get_all_attrs(external_impl), + AttributeKind::EiiImpls(impls) if impls.iter().any(|i| matches!(i.resolution, EiiImplResolution::Macro(_))) + ) + { + tcx.dcx().emit_err(EiiWithGenerics { + span: tcx.def_span(external_impl), + attr: eii_attr_span, + eii_name, + impl_name: tcx.item_name(external_impl), + }); + } + + Ok(()) +} + fn check_early_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, external_impl: LocalDefId, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 7d4f65434dc4..185a822bdfa6 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1652,3 +1652,15 @@ pub(crate) struct LifetimesOrBoundsMismatchOnEii { pub bounds_span: Vec, pub ident: Symbol, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_eii_with_generics)] +#[help] +pub(crate) struct EiiWithGenerics { + #[primary_span] + pub span: Span, + #[label] + pub attr: Span, + pub eii_name: Symbol, + pub impl_name: Symbol, +} diff --git a/tests/ui/eii/type_checking/generic_implementation.rs b/tests/ui/eii/type_checking/generic_implementation.rs new file mode 100644 index 000000000000..489fd2e645d8 --- /dev/null +++ b/tests/ui/eii/type_checking/generic_implementation.rs @@ -0,0 +1,13 @@ +//@ check-fail +// Check that type parameters on EIIs are properly rejected. +// Specifically a regression test for https://github.com/rust-lang/rust/issues/149983. +#![feature(extern_item_impls)] + +#[eii] +fn foo(); + +#[foo] +fn foo_impl() {} +//~^ ERROR `foo_impl` cannot have generic parameters other than lifetimes + +fn main() {} diff --git a/tests/ui/eii/type_checking/generic_implementation.stderr b/tests/ui/eii/type_checking/generic_implementation.stderr new file mode 100644 index 000000000000..17a71998423d --- /dev/null +++ b/tests/ui/eii/type_checking/generic_implementation.stderr @@ -0,0 +1,12 @@ +error: `foo_impl` cannot have generic parameters other than lifetimes + --> $DIR/generic_implementation.rs:10:1 + | +LL | #[foo] + | ------ required by this attribute +LL | fn foo_impl() {} + | ^^^^^^^^^^^^^^^^ + | + = help: `#[foo]` marks the implementation of an "externally implementable item" + +error: aborting due to 1 previous error +