Skip to content

Commit b3477f9

Browse files
committed
ensure generics are still properly reported on EII *implementations*, and test this
1 parent 6996af3 commit b3477f9

File tree

5 files changed

+78
-2
lines changed

5 files changed

+78
-2
lines changed

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported
165165
hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice
166166
.label = parameter captured again here
167167
168+
hir_analysis_eii_with_generics =
169+
`{$impl_name}` cannot have generic parameters other than lifetimes
170+
.label = required by this attribute
171+
.help = `#[{$eii_name}]` marks the implementation of an "externally implementable item"
172+
168173
hir_analysis_empty_specialization = specialization impl does not specialize any associated items
169174
.note = impl is a specialization of this impl
170175

compiler/rustc_hir_analysis/src/check/compare_eii.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ use std::iter;
88

99
use rustc_data_structures::fx::FxIndexSet;
1010
use rustc_errors::{Applicability, E0806, struct_span_code_err};
11+
use rustc_hir::attrs::{AttributeKind, EiiImplResolution};
1112
use rustc_hir::def_id::{DefId, LocalDefId};
12-
use rustc_hir::{self as hir, FnSig, HirId, ItemKind};
13+
use rustc_hir::{self as hir, FnSig, HirId, ItemKind, find_attr};
1314
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
1415
use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
1516
use rustc_middle::ty::error::{ExpectedFound, TypeError};
@@ -24,7 +25,7 @@ use super::potentially_plural_count;
2425
use crate::check::compare_impl_item::{
2526
CheckNumberOfEarlyBoundRegionsError, check_number_of_early_bound_regions,
2627
};
27-
use crate::errors::LifetimesOrBoundsMismatchOnEii;
28+
use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii};
2829

2930
/// Checks whether the signature of some `external_impl`, matches
3031
/// the signature of `declaration`, which it is supposed to be compatible
@@ -154,11 +155,44 @@ fn check_is_structurally_compatible<'tcx>(
154155
eii_name: Symbol,
155156
eii_attr_span: Span,
156157
) -> Result<(), ErrorGuaranteed> {
158+
check_no_generics(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
157159
check_number_of_arguments(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
158160
check_early_region_bounds(tcx, external_impl, declaration, eii_attr_span)?;
159161
Ok(())
160162
}
161163

164+
/// externally implementable items can't have generics
165+
fn check_no_generics<'tcx>(
166+
tcx: TyCtxt<'tcx>,
167+
external_impl: LocalDefId,
168+
_declaration: DefId,
169+
eii_name: Symbol,
170+
eii_attr_span: Span,
171+
) -> Result<(), ErrorGuaranteed> {
172+
let generics = tcx.generics_of(external_impl);
173+
if generics.own_requires_monomorphization()
174+
// When an EII implementation is automatically generated by the `#[eii]` macro,
175+
// it will directly refer to the foreign item, not through a macro.
176+
// We don't want to emit this error if it's an implementation that's generated by the `#[eii]` macro,
177+
// since in that case it looks like a duplicate error: the declaration of the EII already can't contain generics.
178+
// So, we check here if at least one of the eii impls has ImplResolution::Macro, which indicates it's
179+
// not generated as part of the declaration.
180+
&& find_attr!(
181+
tcx.get_all_attrs(external_impl),
182+
AttributeKind::EiiImpls(impls) if impls.iter().any(|i| matches!(i.resolution, EiiImplResolution::Macro(_)))
183+
)
184+
{
185+
tcx.dcx().emit_err(EiiWithGenerics {
186+
span: tcx.def_span(external_impl),
187+
attr: eii_attr_span,
188+
eii_name,
189+
impl_name: tcx.item_name(external_impl),
190+
});
191+
}
192+
193+
Ok(())
194+
}
195+
162196
fn check_early_region_bounds<'tcx>(
163197
tcx: TyCtxt<'tcx>,
164198
external_impl: LocalDefId,

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,3 +1652,15 @@ pub(crate) struct LifetimesOrBoundsMismatchOnEii {
16521652
pub bounds_span: Vec<Span>,
16531653
pub ident: Symbol,
16541654
}
1655+
1656+
#[derive(Diagnostic)]
1657+
#[diag(hir_analysis_eii_with_generics)]
1658+
#[help]
1659+
pub(crate) struct EiiWithGenerics {
1660+
#[primary_span]
1661+
pub span: Span,
1662+
#[label]
1663+
pub attr: Span,
1664+
pub eii_name: Symbol,
1665+
pub impl_name: Symbol,
1666+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//@ check-fail
2+
// Check that type parameters on EIIs are properly rejected.
3+
// Specifically a regression test for https://github.com/rust-lang/rust/issues/149983.
4+
#![feature(extern_item_impls)]
5+
6+
#[eii]
7+
fn foo();
8+
9+
#[foo]
10+
fn foo_impl<T>() {}
11+
//~^ ERROR `foo_impl` cannot have generic parameters other than lifetimes
12+
13+
fn main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: `foo_impl` cannot have generic parameters other than lifetimes
2+
--> $DIR/generic_implementation.rs:10:1
3+
|
4+
LL | #[foo]
5+
| ------ required by this attribute
6+
LL | fn foo_impl<T>() {}
7+
| ^^^^^^^^^^^^^^^^
8+
|
9+
= help: `#[foo]` marks the implementation of an "externally implementable item"
10+
11+
error: aborting due to 1 previous error
12+

0 commit comments

Comments
 (0)