Skip to content

Commit fb045e8

Browse files
committed
Move check_mod_impl_wf query call out of track_errors and bubble errors up instead.
1 parent 2423411 commit fb045e8

File tree

7 files changed

+163
-75
lines changed

7 files changed

+163
-75
lines changed

compiler/rustc_hir_analysis/src/impl_wf_check.rs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_hir::def::DefKind;
1717
use rustc_hir::def_id::{LocalDefId, LocalModDefId};
1818
use rustc_middle::query::Providers;
1919
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
20-
use rustc_span::{Span, Symbol};
20+
use rustc_span::{ErrorGuaranteed, Span, Symbol};
2121

2222
mod min_specialization;
2323

@@ -51,24 +51,29 @@ mod min_specialization;
5151
/// impl<'a> Trait<Foo> for Bar { type X = &'a i32; }
5252
/// // ^ 'a is unused and appears in assoc type, error
5353
/// ```
54-
fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
54+
fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) -> Result<(), ErrorGuaranteed> {
5555
let min_specialization = tcx.features().min_specialization;
5656
let module = tcx.hir_module_items(module_def_id);
57+
let mut res = Ok(());
5758
for id in module.items() {
5859
if matches!(tcx.def_kind(id.owner_id), DefKind::Impl { .. }) {
59-
enforce_impl_params_are_constrained(tcx, id.owner_id.def_id);
60+
res = res.and(enforce_impl_params_are_constrained(tcx, id.owner_id.def_id));
6061
if min_specialization {
61-
check_min_specialization(tcx, id.owner_id.def_id);
62+
res = res.and(check_min_specialization(tcx, id.owner_id.def_id));
6263
}
6364
}
6465
}
66+
res
6567
}
6668

6769
pub fn provide(providers: &mut Providers) {
6870
*providers = Providers { check_mod_impl_wf, ..*providers };
6971
}
7072

71-
fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) {
73+
fn enforce_impl_params_are_constrained(
74+
tcx: TyCtxt<'_>,
75+
impl_def_id: LocalDefId,
76+
) -> Result<(), ErrorGuaranteed> {
7277
// Every lifetime used in an associated type must be constrained.
7378
let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity();
7479
if impl_self_ty.references_error() {
@@ -80,7 +85,10 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
8085
"potentially unconstrained type parameters weren't evaluated: {impl_self_ty:?}",
8186
),
8287
);
83-
return;
88+
// This is super fishy, but our current `rustc_hir_analysis::check_crate` pipeline depends on
89+
// `type_of` having been called much earlier, and thus this value being read from cache.
90+
// Compilation must continue in order for other important diagnostics to keep showing up.
91+
return Ok(());
8492
}
8593
let impl_generics = tcx.generics_of(impl_def_id);
8694
let impl_predicates = tcx.predicates_of(impl_def_id);
@@ -113,41 +121,48 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
113121
})
114122
.collect();
115123

124+
let mut res = Ok(());
116125
for param in &impl_generics.params {
117126
match param.kind {
118127
// Disallow ANY unconstrained type parameters.
119128
ty::GenericParamDefKind::Type { .. } => {
120129
let param_ty = ty::ParamTy::for_def(param);
121130
if !input_parameters.contains(&cgp::Parameter::from(param_ty)) {
122-
report_unused_parameter(tcx, tcx.def_span(param.def_id), "type", param_ty.name);
131+
res = Err(report_unused_parameter(
132+
tcx,
133+
tcx.def_span(param.def_id),
134+
"type",
135+
param_ty.name,
136+
));
123137
}
124138
}
125139
ty::GenericParamDefKind::Lifetime => {
126140
let param_lt = cgp::Parameter::from(param.to_early_bound_region_data());
127141
if lifetimes_in_associated_types.contains(&param_lt) && // (*)
128142
!input_parameters.contains(&param_lt)
129143
{
130-
report_unused_parameter(
144+
res = Err(report_unused_parameter(
131145
tcx,
132146
tcx.def_span(param.def_id),
133147
"lifetime",
134148
param.name,
135-
);
149+
));
136150
}
137151
}
138152
ty::GenericParamDefKind::Const { .. } => {
139153
let param_ct = ty::ParamConst::for_def(param);
140154
if !input_parameters.contains(&cgp::Parameter::from(param_ct)) {
141-
report_unused_parameter(
155+
res = Err(report_unused_parameter(
142156
tcx,
143157
tcx.def_span(param.def_id),
144158
"const",
145159
param_ct.name,
146-
);
160+
));
147161
}
148162
}
149163
}
150164
}
165+
res
151166

152167
// (*) This is a horrible concession to reality. I think it'd be
153168
// better to just ban unconstrained lifetimes outright, but in
@@ -169,7 +184,12 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
169184
// used elsewhere are not projected back out.
170185
}
171186

172-
fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol) {
187+
fn report_unused_parameter(
188+
tcx: TyCtxt<'_>,
189+
span: Span,
190+
kind: &str,
191+
name: Symbol,
192+
) -> ErrorGuaranteed {
173193
let mut err = struct_span_code_err!(
174194
tcx.dcx(),
175195
span,
@@ -188,5 +208,5 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol
188208
"proving the result of expressions other than the parameter are unique is not supported",
189209
);
190210
}
191-
err.emit();
211+
err.emit()
192212
}

compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs

Lines changed: 82 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,14 @@ use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
8282
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
8383
use rustc_trait_selection::traits::{self, translate_args_with_cause, wf, ObligationCtxt};
8484

85-
pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) {
85+
pub(super) fn check_min_specialization(
86+
tcx: TyCtxt<'_>,
87+
impl_def_id: LocalDefId,
88+
) -> Result<(), ErrorGuaranteed> {
8689
if let Some(node) = parent_specialization_node(tcx, impl_def_id) {
87-
check_always_applicable(tcx, impl_def_id, node);
90+
check_always_applicable(tcx, impl_def_id, node)?;
8891
}
92+
Ok(())
8993
}
9094

9195
fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Option<Node> {
@@ -109,52 +113,69 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti
109113

110114
/// Check that `impl1` is a sound specialization
111115
#[instrument(level = "debug", skip(tcx))]
112-
fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node) {
116+
fn check_always_applicable(
117+
tcx: TyCtxt<'_>,
118+
impl1_def_id: LocalDefId,
119+
impl2_node: Node,
120+
) -> Result<(), ErrorGuaranteed> {
113121
let span = tcx.def_span(impl1_def_id);
114-
check_has_items(tcx, impl1_def_id, impl2_node, span);
115-
116-
if let Ok((impl1_args, impl2_args)) = get_impl_args(tcx, impl1_def_id, impl2_node) {
117-
let impl2_def_id = impl2_node.def_id();
118-
debug!(?impl2_def_id, ?impl2_args);
119-
120-
let parent_args = if impl2_node.is_from_trait() {
121-
impl2_args.to_vec()
122-
} else {
123-
unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args)
124-
};
125-
126-
check_constness(tcx, impl1_def_id, impl2_node, span);
127-
check_static_lifetimes(tcx, &parent_args, span);
128-
check_duplicate_params(tcx, impl1_args, &parent_args, span);
129-
check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span);
130-
}
122+
let mut res = check_has_items(tcx, impl1_def_id, impl2_node, span);
123+
124+
let (impl1_args, impl2_args) = get_impl_args(tcx, impl1_def_id, impl2_node)?;
125+
let impl2_def_id = impl2_node.def_id();
126+
debug!(?impl2_def_id, ?impl2_args);
127+
128+
let parent_args = if impl2_node.is_from_trait() {
129+
impl2_args.to_vec()
130+
} else {
131+
unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args)
132+
};
133+
134+
res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span));
135+
res = res.and(check_static_lifetimes(tcx, &parent_args, span));
136+
res = res.and(check_duplicate_params(tcx, impl1_args, &parent_args, span));
137+
res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span));
138+
139+
res
131140
}
132141

133-
fn check_has_items(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, span: Span) {
142+
fn check_has_items(
143+
tcx: TyCtxt<'_>,
144+
impl1_def_id: LocalDefId,
145+
impl2_node: Node,
146+
span: Span,
147+
) -> Result<(), ErrorGuaranteed> {
134148
if let Node::Impl(impl2_id) = impl2_node
135149
&& tcx.associated_item_def_ids(impl1_def_id).is_empty()
136150
{
137151
let base_impl_span = tcx.def_span(impl2_id);
138-
tcx.dcx().emit_err(errors::EmptySpecialization { span, base_impl_span });
152+
return Err(tcx.dcx().emit_err(errors::EmptySpecialization { span, base_impl_span }));
139153
}
154+
Ok(())
140155
}
141156

142157
/// Check that the specializing impl `impl1` is at least as const as the base
143158
/// impl `impl2`
144-
fn check_constness(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, span: Span) {
159+
fn check_constness(
160+
tcx: TyCtxt<'_>,
161+
impl1_def_id: LocalDefId,
162+
impl2_node: Node,
163+
span: Span,
164+
) -> Result<(), ErrorGuaranteed> {
145165
if impl2_node.is_from_trait() {
146166
// This isn't a specialization
147-
return;
167+
return Ok(());
148168
}
149169

150170
let impl1_constness = tcx.constness(impl1_def_id.to_def_id());
151171
let impl2_constness = tcx.constness(impl2_node.def_id());
152172

153173
if let hir::Constness::Const = impl2_constness {
154174
if let hir::Constness::NotConst = impl1_constness {
155-
tcx.dcx().emit_err(errors::ConstSpecialize { span });
175+
return Err(tcx.dcx().emit_err(errors::ConstSpecialize { span }));
156176
}
157177
}
178+
Ok(())
158179
}
159180

160181
/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two
@@ -290,15 +311,17 @@ fn check_duplicate_params<'tcx>(
290311
impl1_args: GenericArgsRef<'tcx>,
291312
parent_args: &Vec<GenericArg<'tcx>>,
292313
span: Span,
293-
) {
314+
) -> Result<(), ErrorGuaranteed> {
294315
let mut base_params = cgp::parameters_for(parent_args, true);
295316
base_params.sort_by_key(|param| param.0);
296317
if let (_, [duplicate, ..]) = base_params.partition_dedup() {
297318
let param = impl1_args[duplicate.0 as usize];
298-
tcx.dcx()
319+
return Err(tcx
320+
.dcx()
299321
.struct_span_err(span, format!("specializing impl repeats parameter `{param}`"))
300-
.emit();
322+
.emit());
301323
}
324+
Ok(())
302325
}
303326

304327
/// Check that `'static` lifetimes are not introduced by the specializing impl.
@@ -313,10 +336,11 @@ fn check_static_lifetimes<'tcx>(
313336
tcx: TyCtxt<'tcx>,
314337
parent_args: &Vec<GenericArg<'tcx>>,
315338
span: Span,
316-
) {
339+
) -> Result<(), ErrorGuaranteed> {
317340
if tcx.any_free_region_meets(parent_args, |r| r.is_static()) {
318-
tcx.dcx().emit_err(errors::StaticSpecialize { span });
341+
return Err(tcx.dcx().emit_err(errors::StaticSpecialize { span }));
319342
}
343+
Ok(())
320344
}
321345

322346
/// Check whether predicates on the specializing impl (`impl1`) are allowed.
@@ -337,7 +361,7 @@ fn check_predicates<'tcx>(
337361
impl2_node: Node,
338362
impl2_args: GenericArgsRef<'tcx>,
339363
span: Span,
340-
) {
364+
) -> Result<(), ErrorGuaranteed> {
341365
let impl1_predicates: Vec<_> = traits::elaborate(
342366
tcx,
343367
tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_args).into_iter(),
@@ -399,14 +423,16 @@ fn check_predicates<'tcx>(
399423
}
400424
impl2_predicates.extend(traits::elaborate(tcx, always_applicable_traits));
401425

426+
let mut res = Ok(());
402427
for (clause, span) in impl1_predicates {
403428
if !impl2_predicates
404429
.iter()
405430
.any(|pred2| trait_predicates_eq(tcx, clause.as_predicate(), *pred2, span))
406431
{
407-
check_specialization_on(tcx, clause, span)
432+
res = res.and(check_specialization_on(tcx, clause, span))
408433
}
409434
}
435+
res
410436
}
411437

412438
/// Checks if some predicate on the specializing impl (`predicate1`) is the same
@@ -443,37 +469,43 @@ fn trait_predicates_eq<'tcx>(
443469
}
444470

445471
#[instrument(level = "debug", skip(tcx))]
446-
fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, clause: ty::Clause<'tcx>, span: Span) {
472+
fn check_specialization_on<'tcx>(
473+
tcx: TyCtxt<'tcx>,
474+
clause: ty::Clause<'tcx>,
475+
span: Span,
476+
) -> Result<(), ErrorGuaranteed> {
447477
match clause.kind().skip_binder() {
448478
// Global predicates are either always true or always false, so we
449479
// are fine to specialize on.
450-
_ if clause.is_global() => (),
480+
_ if clause.is_global() => Ok(()),
451481
// We allow specializing on explicitly marked traits with no associated
452482
// items.
453483
ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity: _ }) => {
454-
if !matches!(
484+
if matches!(
455485
trait_specialization_kind(tcx, clause),
456486
Some(TraitSpecializationKind::Marker)
457487
) {
458-
tcx.dcx()
488+
Ok(())
489+
} else {
490+
Err(tcx
491+
.dcx()
459492
.struct_span_err(
460493
span,
461494
format!(
462495
"cannot specialize on trait `{}`",
463496
tcx.def_path_str(trait_ref.def_id),
464497
),
465498
)
466-
.emit();
499+
.emit())
467500
}
468501
}
469-
ty::ClauseKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => {
470-
tcx.dcx()
471-
.struct_span_err(
472-
span,
473-
format!("cannot specialize on associated type `{projection_ty} == {term}`",),
474-
)
475-
.emit();
476-
}
502+
ty::ClauseKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => Err(tcx
503+
.dcx()
504+
.struct_span_err(
505+
span,
506+
format!("cannot specialize on associated type `{projection_ty} == {term}`",),
507+
)
508+
.emit()),
477509
ty::ClauseKind::ConstArgHasType(..) => {
478510
// FIXME(min_specialization), FIXME(const_generics):
479511
// It probably isn't right to allow _every_ `ConstArgHasType` but I am somewhat unsure
@@ -483,12 +515,12 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, clause: ty::Clause<'tcx>, sp
483515
// While we do not support constructs like `<T, const N: T>` there is probably no risk of
484516
// soundness bugs, but when we support generic const parameter types this will need to be
485517
// revisited.
518+
Ok(())
486519
}
487-
_ => {
488-
tcx.dcx()
489-
.struct_span_err(span, format!("cannot specialize on predicate `{clause}`"))
490-
.emit();
491-
}
520+
_ => Err(tcx
521+
.dcx()
522+
.struct_span_err(span, format!("cannot specialize on predicate `{clause}`"))
523+
.emit()),
492524
}
493525
}
494526

0 commit comments

Comments
 (0)