Skip to content

Commit 2d602ea

Browse files
Do not project when there are unconstrained impl params
1 parent ab3924b commit 2d602ea

26 files changed

+214
-283
lines changed

Diff for: compiler/rustc_hir_analysis/src/impl_wf_check.rs

+92-31
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,24 @@ pub(crate) fn check_impl_wf(
5757
tcx: TyCtxt<'_>,
5858
impl_def_id: LocalDefId,
5959
) -> Result<(), ErrorGuaranteed> {
60-
let min_specialization = tcx.features().min_specialization();
61-
let mut res = Ok(());
6260
debug_assert_matches!(tcx.def_kind(impl_def_id), DefKind::Impl { .. });
63-
res = res.and(enforce_impl_params_are_constrained(tcx, impl_def_id));
64-
if min_specialization {
61+
62+
// Check that the args are constrained. We queryfied the check for ty/const params
63+
// since unconstrained type/const params cause ICEs in projection, so we want to
64+
// detect those specifically and project those to `TyKind::Error`.
65+
let mut res = tcx.ensure().enforce_impl_non_lifetime_params_are_constrained(impl_def_id);
66+
res = res.and(enforce_impl_lifetime_params_are_constrained(tcx, impl_def_id));
67+
68+
if tcx.features().min_specialization() {
6569
res = res.and(check_min_specialization(tcx, impl_def_id));
6670
}
67-
6871
res
6972
}
7073

71-
fn enforce_impl_params_are_constrained(
74+
pub(crate) fn enforce_impl_lifetime_params_are_constrained(
7275
tcx: TyCtxt<'_>,
7376
impl_def_id: LocalDefId,
7477
) -> Result<(), ErrorGuaranteed> {
75-
// Every lifetime used in an associated type must be constrained.
7678
let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity();
7779
if impl_self_ty.references_error() {
7880
// Don't complain about unconstrained type params when self ty isn't known due to errors.
@@ -88,6 +90,7 @@ fn enforce_impl_params_are_constrained(
8890
// Compilation must continue in order for other important diagnostics to keep showing up.
8991
return Ok(());
9092
}
93+
9194
let impl_generics = tcx.generics_of(impl_def_id);
9295
let impl_predicates = tcx.predicates_of(impl_def_id);
9396
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::instantiate_identity);
@@ -121,6 +124,84 @@ fn enforce_impl_params_are_constrained(
121124
})
122125
.collect();
123126

127+
let mut res = Ok(());
128+
for param in &impl_generics.own_params {
129+
match param.kind {
130+
ty::GenericParamDefKind::Lifetime => {
131+
let param_lt = cgp::Parameter::from(param.to_early_bound_region_data());
132+
if lifetimes_in_associated_types.contains(&param_lt) // (*)
133+
&& !input_parameters.contains(&param_lt)
134+
{
135+
let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
136+
span: tcx.def_span(param.def_id),
137+
param_name: param.name,
138+
param_def_kind: tcx.def_descr(param.def_id),
139+
const_param_note: false,
140+
const_param_note2: false,
141+
});
142+
diag.code(E0207);
143+
res = Err(diag.emit());
144+
}
145+
// (*) This is a horrible concession to reality. I think it'd be
146+
// better to just ban unconstrained lifetimes outright, but in
147+
// practice people do non-hygienic macros like:
148+
//
149+
// ```
150+
// macro_rules! __impl_slice_eq1 {
151+
// ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
152+
// impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
153+
// ....
154+
// }
155+
// }
156+
// }
157+
// ```
158+
//
159+
// In a concession to backwards compatibility, we continue to
160+
// permit those, so long as the lifetimes aren't used in
161+
// associated types. I believe this is sound, because lifetimes
162+
// used elsewhere are not projected back out.
163+
}
164+
ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => {
165+
// Enforced in `enforce_impl_non_lifetime_params_are_constrained`.
166+
}
167+
}
168+
}
169+
res
170+
}
171+
172+
pub(crate) fn enforce_impl_non_lifetime_params_are_constrained(
173+
tcx: TyCtxt<'_>,
174+
impl_def_id: LocalDefId,
175+
) -> Result<(), ErrorGuaranteed> {
176+
let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity();
177+
if impl_self_ty.references_error() {
178+
// Don't complain about unconstrained type params when self ty isn't known due to errors.
179+
// (#36836)
180+
tcx.dcx().span_delayed_bug(
181+
tcx.def_span(impl_def_id),
182+
format!(
183+
"potentially unconstrained type parameters weren't evaluated: {impl_self_ty:?}",
184+
),
185+
);
186+
// This is super fishy, but our current `rustc_hir_analysis::check_crate` pipeline depends on
187+
// `type_of` having been called much earlier, and thus this value being read from cache.
188+
// Compilation must continue in order for other important diagnostics to keep showing up.
189+
return Ok(());
190+
}
191+
let impl_generics = tcx.generics_of(impl_def_id);
192+
let impl_predicates = tcx.predicates_of(impl_def_id);
193+
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::instantiate_identity);
194+
195+
impl_trait_ref.error_reported()?;
196+
197+
let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref);
198+
cgp::identify_constrained_generic_params(
199+
tcx,
200+
impl_predicates,
201+
impl_trait_ref,
202+
&mut input_parameters,
203+
);
204+
124205
let mut res = Ok(());
125206
for param in &impl_generics.own_params {
126207
let err = match param.kind {
@@ -129,15 +210,14 @@ fn enforce_impl_params_are_constrained(
129210
let param_ty = ty::ParamTy::for_def(param);
130211
!input_parameters.contains(&cgp::Parameter::from(param_ty))
131212
}
132-
ty::GenericParamDefKind::Lifetime => {
133-
let param_lt = cgp::Parameter::from(param.to_early_bound_region_data());
134-
lifetimes_in_associated_types.contains(&param_lt) && // (*)
135-
!input_parameters.contains(&param_lt)
136-
}
137213
ty::GenericParamDefKind::Const { .. } => {
138214
let param_ct = ty::ParamConst::for_def(param);
139215
!input_parameters.contains(&cgp::Parameter::from(param_ct))
140216
}
217+
ty::GenericParamDefKind::Lifetime => {
218+
// Enforced in `enforce_impl_type_params_are_constrained`.
219+
false
220+
}
141221
};
142222
if err {
143223
let const_param_note = matches!(param.kind, ty::GenericParamDefKind::Const { .. });
@@ -153,23 +233,4 @@ fn enforce_impl_params_are_constrained(
153233
}
154234
}
155235
res
156-
157-
// (*) This is a horrible concession to reality. I think it'd be
158-
// better to just ban unconstrained lifetimes outright, but in
159-
// practice people do non-hygienic macros like:
160-
//
161-
// ```
162-
// macro_rules! __impl_slice_eq1 {
163-
// ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
164-
// impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
165-
// ....
166-
// }
167-
// }
168-
// }
169-
// ```
170-
//
171-
// In a concession to backwards compatibility, we continue to
172-
// permit those, so long as the lifetimes aren't used in
173-
// associated types. I believe this is sound, because lifetimes
174-
// used elsewhere are not projected back out.
175236
}

Diff for: compiler/rustc_hir_analysis/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ pub fn provide(providers: &mut Providers) {
128128
hir_wf_check::provide(providers);
129129
*providers = Providers {
130130
inherit_sig_for_delegation_item: delegation::inherit_sig_for_delegation_item,
131+
enforce_impl_non_lifetime_params_are_constrained:
132+
impl_wf_check::enforce_impl_non_lifetime_params_are_constrained,
131133
..*providers
132134
};
133135
}

Diff for: compiler/rustc_middle/src/query/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,11 @@ rustc_queries! {
17191719
ensure_forwards_result_if_red
17201720
}
17211721

1722+
query enforce_impl_non_lifetime_params_are_constrained(key: LocalDefId) -> Result<(), ErrorGuaranteed> {
1723+
desc { |tcx| "checking that `{}`'s generics are constrained by the impl header", tcx.def_path_str(key) }
1724+
ensure_forwards_result_if_red
1725+
}
1726+
17221727
// The `DefId`s of all non-generic functions and statics in the given crate
17231728
// that can be reached from outside the crate.
17241729
//

Diff for: compiler/rustc_trait_selection/src/traits/project.rs

+34-29
Original file line numberDiff line numberDiff line change
@@ -950,39 +950,45 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
950950
//
951951
// NOTE: This should be kept in sync with the similar code in
952952
// `rustc_ty_utils::instance::resolve_associated_item()`.
953-
let node_item = specialization_graph::assoc_def(
953+
match specialization_graph::assoc_def(
954954
selcx.tcx(),
955955
impl_data.impl_def_id,
956956
obligation.predicate.def_id,
957-
)
958-
.map_err(|ErrorGuaranteed { .. }| ())?;
959-
960-
if node_item.is_final() {
961-
// Non-specializable items are always projectable.
962-
true
963-
} else {
964-
// Only reveal a specializable default if we're past type-checking
965-
// and the obligation is monomorphic, otherwise passes such as
966-
// transmute checking and polymorphic MIR optimizations could
967-
// get a result which isn't correct for all monomorphizations.
968-
match selcx.infcx.typing_mode() {
969-
TypingMode::Coherence
970-
| TypingMode::Analysis { .. }
971-
| TypingMode::PostBorrowckAnalysis { .. } => {
972-
debug!(
973-
assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id),
974-
?obligation.predicate,
975-
"assemble_candidates_from_impls: not eligible due to default",
976-
);
977-
false
978-
}
979-
TypingMode::PostAnalysis => {
980-
// NOTE(eddyb) inference variables can resolve to parameters, so
981-
// assume `poly_trait_ref` isn't monomorphic, if it contains any.
982-
let poly_trait_ref = selcx.infcx.resolve_vars_if_possible(trait_ref);
983-
!poly_trait_ref.still_further_specializable()
957+
) {
958+
Ok(node_item) => {
959+
if node_item.is_final() {
960+
// Non-specializable items are always projectable.
961+
true
962+
} else {
963+
// Only reveal a specializable default if we're past type-checking
964+
// and the obligation is monomorphic, otherwise passes such as
965+
// transmute checking and polymorphic MIR optimizations could
966+
// get a result which isn't correct for all monomorphizations.
967+
match selcx.infcx.typing_mode() {
968+
TypingMode::Coherence
969+
| TypingMode::Analysis { .. }
970+
| TypingMode::PostBorrowckAnalysis { .. } => {
971+
debug!(
972+
assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id),
973+
?obligation.predicate,
974+
"not eligible due to default",
975+
);
976+
false
977+
}
978+
TypingMode::PostAnalysis => {
979+
// NOTE(eddyb) inference variables can resolve to parameters, so
980+
// assume `poly_trait_ref` isn't monomorphic, if it contains any.
981+
let poly_trait_ref =
982+
selcx.infcx.resolve_vars_if_possible(trait_ref);
983+
!poly_trait_ref.still_further_specializable()
984+
}
985+
}
984986
}
985987
}
988+
// Always project `ErrorGuaranteed`, since this will just help
989+
// us propagate `TyKind::Error` around which suppresses ICEs
990+
// and spurious, unrelated inference errors.
991+
Err(ErrorGuaranteed { .. }) => true,
986992
}
987993
}
988994
ImplSource::Builtin(BuiltinImplSource::Misc, _) => {
@@ -2014,7 +2020,6 @@ fn confirm_impl_candidate<'cx, 'tcx>(
20142020
Ok(assoc_ty) => assoc_ty,
20152021
Err(guar) => return Progress::error(tcx, guar),
20162022
};
2017-
20182023
if !assoc_ty.item.defaultness(tcx).has_value() {
20192024
// This means that the impl is missing a definition for the
20202025
// associated type. This error will be reported by the type

Diff for: compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs

+14
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,12 @@ pub(crate) fn assoc_def(
376376
// If there is no such item in that impl, this function will fail with a
377377
// cycle error if the specialization graph is currently being built.
378378
if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) {
379+
// Ensure that the impl is constrained, otherwise projection may give us
380+
// bad unconstrained infer vars.
381+
if let Some(impl_def_id) = impl_def_id.as_local() {
382+
tcx.ensure().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?;
383+
}
384+
379385
let item = tcx.associated_item(impl_item_id);
380386
let impl_node = Node::Impl(impl_def_id);
381387
return Ok(LeafDef {
@@ -391,6 +397,14 @@ pub(crate) fn assoc_def(
391397

392398
let ancestors = trait_def.ancestors(tcx, impl_def_id)?;
393399
if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) {
400+
// Ensure that the impl is constrained, otherwise projection may give us
401+
// bad unconstrained infer vars.
402+
if assoc_item.item.container == ty::AssocItemContainer::Impl
403+
&& let Some(impl_def_id) = assoc_item.item.container_id(tcx).as_local()
404+
{
405+
tcx.ensure().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?;
406+
}
407+
394408
Ok(assoc_item)
395409
} else {
396410
// This is saying that neither the trait nor

Diff for: tests/crashes/123141.rs

-23
This file was deleted.

Diff for: tests/crashes/125874.rs

-22
This file was deleted.

Diff for: tests/crashes/126942.rs

-11
This file was deleted.

Diff for: tests/crashes/127804.rs

-12
This file was deleted.

Diff for: tests/crashes/130967.rs

-13
This file was deleted.

0 commit comments

Comments
 (0)