Skip to content

Commit ac63db7

Browse files
committed
fix problems with type checking
1 parent 83d1fbc commit ac63db7

File tree

4 files changed

+244
-196
lines changed

4 files changed

+244
-196
lines changed

compiler/rustc_hir_analysis/src/check/compare_eii.rs

Lines changed: 157 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
//! This module is very similar to `compare_impl_item`.
2+
//! Most logic is taken from there,
3+
//! since in a very similar way we're comparing some declaration of a signature to an implementation.
4+
//! The major difference is that we don't bother with self types, since for EIIs we're comparing freestanding item.
5+
16
use std::borrow::Cow;
27
use std::iter;
38

@@ -8,17 +13,20 @@ use rustc_hir::{self as hir, FnSig, HirId, ItemKind};
813
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
914
use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
1015
use rustc_middle::ty::error::{ExpectedFound, TypeError};
11-
use rustc_middle::ty::{self, TyCtxt, TypingMode};
16+
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode};
1217
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
1318
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
1419
use rustc_trait_selection::regions::InferCtxtRegionExt;
15-
use rustc_trait_selection::traits::ObligationCtxt;
20+
use rustc_trait_selection::traits::{self, ObligationCtxt};
1621
use tracing::{debug, instrument};
1722

1823
use super::potentially_plural_count;
24+
use crate::check::compare_impl_item::{
25+
CheckNumberOfEarlyBoundRegionsError, check_number_of_early_bound_regions,
26+
};
1927
use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii};
2028

21-
/// checks whether the signature of some `external_impl`, matches
29+
/// Checks whether the signature of some `external_impl`, matches
2230
/// the signature of `declaration`, which it is supposed to be compatible
2331
/// with in order to implement the item.
2432
pub(crate) fn compare_eii_function_types<'tcx>(
@@ -43,33 +51,43 @@ pub(crate) fn compare_eii_function_types<'tcx>(
4351
let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis());
4452
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
4553

46-
// We now need to check that the signature of the impl method is
47-
// compatible with that of the trait method. We do this by
54+
// We now need to check that the signature of the implementation is
55+
// compatible with that of the declaration. We do this by
4856
// checking that `impl_fty <: trait_fty`.
4957
//
50-
// FIXME: We manually instantiate the trait method here as we need
58+
// FIXME: We manually instantiate the declaration here as we need
5159
// to manually compute its implied bounds. Otherwise this could just
5260
// be ocx.sub(impl_sig, trait_sig).
5361

54-
let wf_tys = FxIndexSet::default();
62+
let mut wf_tys = FxIndexSet::default();
5563
let norm_cause = ObligationCause::misc(external_impl_span, external_impl);
5664

5765
let declaration_sig = tcx.fn_sig(declaration).instantiate_identity();
58-
let declaration_sig = infcx.enter_forall_and_leak_universe(declaration_sig);
59-
let declaration_sig = ocx.normalize(&norm_cause, param_env, declaration_sig);
66+
let declaration_sig = tcx.liberate_late_bound_regions(external_impl.into(), declaration_sig);
67+
debug!(?declaration_sig);
6068

61-
let external_impl_sig = infcx.instantiate_binder_with_fresh_vars(
69+
let unnormalized_external_impl_sig = infcx.instantiate_binder_with_fresh_vars(
6270
external_impl_span,
6371
infer::BoundRegionConversionTime::HigherRankedType,
6472
tcx.fn_sig(external_impl).instantiate(
6573
tcx,
6674
infcx.fresh_args_for_item(external_impl_span, external_impl.to_def_id()),
6775
),
6876
);
69-
let external_impl_sig = ocx.normalize(&norm_cause, param_env, external_impl_sig);
77+
let external_impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_external_impl_sig);
7078
debug!(?external_impl_sig);
7179

72-
// FIXME: We'd want to keep more accurate spans than "the method signature" when
80+
// Next, add all inputs and output as well-formed tys. Importantly,
81+
// we have to do this before normalization, since the normalized ty may
82+
// not contain the input parameters. See issue #87748.
83+
wf_tys.extend(declaration_sig.inputs_and_output.iter());
84+
let declaration_sig = ocx.normalize(&norm_cause, param_env, declaration_sig);
85+
// We also have to add the normalized declaration
86+
// as we don't normalize during implied bounds computation.
87+
wf_tys.extend(external_impl_sig.inputs_and_output.iter());
88+
89+
// FIXME: Copied over from compare impl items, same issue:
90+
// We'd want to keep more accurate spans than "the method signature" when
7391
// processing the comparison between the trait and impl fn, but we sadly lose them
7492
// and point at the whole signature when a trait bound or specific input or output
7593
// type would be more appropriate. In other places we have a `Vec<Span>`
@@ -93,6 +111,17 @@ pub(crate) fn compare_eii_function_types<'tcx>(
93111
return Err(emitted);
94112
}
95113

114+
if !(declaration_sig, external_impl_sig).references_error() {
115+
for ty in unnormalized_external_impl_sig.inputs_and_output {
116+
ocx.register_obligation(traits::Obligation::new(
117+
infcx.tcx,
118+
cause.clone(),
119+
param_env,
120+
ty::ClauseKind::WellFormed(ty.into()),
121+
));
122+
}
123+
}
124+
96125
// Check that all obligations are satisfied by the implementation's
97126
// version.
98127
let errors = ocx.evaluate_obligations_error_on_ambiguity();
@@ -116,6 +145,8 @@ pub(crate) fn compare_eii_function_types<'tcx>(
116145
/// Checks a bunch of different properties of the impl/trait methods for
117146
/// compatibility, such as asyncness, number of argument, self receiver kind,
118147
/// and number of early- and late-bound generics.
148+
///
149+
/// Corresponds to `check_method_is_structurally_compatible` for impl method compatibility checks.
119150
fn check_is_structurally_compatible<'tcx>(
120151
tcx: TyCtxt<'tcx>,
121152
external_impl: LocalDefId,
@@ -124,11 +155,12 @@ fn check_is_structurally_compatible<'tcx>(
124155
eii_attr_span: Span,
125156
) -> Result<(), ErrorGuaranteed> {
126157
check_no_generics(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
127-
compare_number_of_method_arguments(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
128-
check_region_bounds_on_impl_item(tcx, external_impl, declaration, eii_attr_span)?;
158+
check_number_of_arguments(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
159+
check_early_region_bounds(tcx, external_impl, declaration, eii_attr_span)?;
129160
Ok(())
130161
}
131162

163+
/// externally implementable items can't have generics
132164
fn check_no_generics<'tcx>(
133165
tcx: TyCtxt<'tcx>,
134166
external_impl: LocalDefId,
@@ -148,7 +180,45 @@ fn check_no_generics<'tcx>(
148180
Ok(())
149181
}
150182

151-
fn compare_number_of_method_arguments<'tcx>(
183+
fn check_early_region_bounds<'tcx>(
184+
tcx: TyCtxt<'tcx>,
185+
external_impl: LocalDefId,
186+
declaration: DefId,
187+
eii_attr_span: Span,
188+
) -> Result<(), ErrorGuaranteed> {
189+
let external_impl_generics = tcx.generics_of(external_impl.to_def_id());
190+
let external_impl_params = external_impl_generics.own_counts().lifetimes;
191+
192+
let declaration_generics = tcx.generics_of(declaration);
193+
let declaration_params = declaration_generics.own_counts().lifetimes;
194+
195+
let Err(CheckNumberOfEarlyBoundRegionsError { span, generics_span, bounds_span, where_span }) =
196+
check_number_of_early_bound_regions(
197+
tcx,
198+
external_impl,
199+
declaration,
200+
external_impl_generics,
201+
external_impl_params,
202+
declaration_generics,
203+
declaration_params,
204+
)
205+
else {
206+
return Ok(());
207+
};
208+
209+
let mut diag = tcx.dcx().create_err(LifetimesOrBoundsMismatchOnEii {
210+
span,
211+
ident: tcx.item_name(external_impl.to_def_id()),
212+
generics_span,
213+
bounds_span,
214+
where_span,
215+
});
216+
217+
diag.span_label(eii_attr_span, format!("required because of this attribute"));
218+
return Err(diag.emit());
219+
}
220+
221+
fn check_number_of_arguments<'tcx>(
152222
tcx: TyCtxt<'tcx>,
153223
external_impl: LocalDefId,
154224
declaration: DefId,
@@ -159,152 +229,91 @@ fn compare_number_of_method_arguments<'tcx>(
159229
let declaration_fty = tcx.fn_sig(declaration);
160230
let declaration_number_args = declaration_fty.skip_binder().inputs().skip_binder().len();
161231
let external_impl_number_args = external_impl_fty.skip_binder().inputs().skip_binder().len();
162-
let external_impl_name = tcx.item_name(external_impl.to_def_id());
163-
164-
if declaration_number_args != external_impl_number_args {
165-
let declaration_span = declaration
166-
.as_local()
167-
.and_then(|def_id| {
168-
let declaration_sig = get_declaration_sig(tcx, def_id).expect("foreign item sig");
169-
let pos = declaration_number_args.saturating_sub(1);
170-
declaration_sig.decl.inputs.get(pos).map(|arg| {
171-
if pos == 0 {
172-
arg.span
173-
} else {
174-
arg.span.with_lo(declaration_sig.decl.inputs[0].span.lo())
175-
}
176-
})
177-
})
178-
.or_else(|| tcx.hir_span_if_local(declaration))
179-
.unwrap_or_else(|| tcx.def_span(declaration));
180-
181-
let (_, external_impl_sig, _, _) = &tcx.hir_expect_item(external_impl).expect_fn();
182-
let pos = external_impl_number_args.saturating_sub(1);
183-
let impl_span = external_impl_sig
184-
.decl
185-
.inputs
186-
.get(pos)
187-
.map(|arg| {
188-
if pos == 0 {
189-
arg.span
190-
} else {
191-
arg.span.with_lo(external_impl_sig.decl.inputs[0].span.lo())
192-
}
193-
})
194-
.unwrap_or_else(|| tcx.def_span(external_impl));
195-
196-
let mut err = struct_span_code_err!(
197-
tcx.dcx(),
198-
impl_span,
199-
E0805,
200-
"`{external_impl_name}` has {} but #[{eii_name}] requires it to have {}",
201-
potentially_plural_count(external_impl_number_args, "parameter"),
202-
declaration_number_args
203-
);
204-
205-
// if let Some(declaration_span) = declaration_span {
206-
err.span_label(
207-
declaration_span,
208-
format!("requires {}", potentially_plural_count(declaration_number_args, "parameter")),
209-
);
210-
// }
211-
212-
err.span_label(
213-
impl_span,
214-
format!(
215-
"expected {}, found {}",
216-
potentially_plural_count(declaration_number_args, "parameter"),
217-
external_impl_number_args
218-
),
219-
);
220232

221-
err.span_label(eii_attr_span, format!("required because of this attribute"));
222-
223-
return Err(err.emit());
233+
// if the number of args are equal, we're trivially done
234+
if declaration_number_args == external_impl_number_args {
235+
Ok(())
236+
} else {
237+
Err(report_number_of_arguments_mismatch(
238+
tcx,
239+
external_impl,
240+
declaration,
241+
eii_name,
242+
eii_attr_span,
243+
declaration_number_args,
244+
external_impl_number_args,
245+
))
224246
}
225-
226-
Ok(())
227247
}
228248

229-
fn check_region_bounds_on_impl_item<'tcx>(
249+
fn report_number_of_arguments_mismatch<'tcx>(
230250
tcx: TyCtxt<'tcx>,
231251
external_impl: LocalDefId,
232252
declaration: DefId,
253+
eii_name: Symbol,
233254
eii_attr_span: Span,
234-
) -> Result<(), ErrorGuaranteed> {
235-
let external_impl_generics = tcx.generics_of(external_impl.to_def_id());
236-
let external_impl_params = external_impl_generics.own_counts().lifetimes;
237-
238-
let declaration_generics = tcx.generics_of(declaration);
239-
let declaration_params = declaration_generics.own_counts().lifetimes;
255+
declaration_number_args: usize,
256+
external_impl_number_args: usize,
257+
) -> ErrorGuaranteed {
258+
let external_impl_name = tcx.item_name(external_impl.to_def_id());
240259

241-
debug!(?declaration_generics, ?external_impl_generics);
242-
243-
// Must have same number of early-bound lifetime parameters.
244-
// Unfortunately, if the user screws up the bounds, then this
245-
// will change classification between early and late. E.g.,
246-
// if in trait we have `<'a,'b:'a>`, and in impl we just have
247-
// `<'a,'b>`, then we have 2 early-bound lifetime parameters
248-
// in trait but 0 in the impl. But if we report "expected 2
249-
// but found 0" it's confusing, because it looks like there
250-
// are zero. Since I don't quite know how to phrase things at
251-
// the moment, give a kind of vague error message.
252-
if declaration_params != external_impl_params {
253-
let span = tcx
254-
.hir_get_generics(external_impl)
255-
.expect("expected impl item to have generics or else we can't compare them")
256-
.span;
257-
258-
let mut generics_span = None;
259-
let mut bounds_span = vec![];
260-
let mut where_span = None;
261-
262-
if let Some(declaration_node) = tcx.hir_get_if_local(declaration)
263-
&& let Some(declaration_generics) = declaration_node.generics()
264-
{
265-
generics_span = Some(declaration_generics.span);
266-
// FIXME: we could potentially look at the impl's bounds to not point at bounds that
267-
// *are* present in the impl.
268-
for p in declaration_generics.predicates {
269-
if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind {
270-
for b in pred.bounds {
271-
if let hir::GenericBound::Outlives(lt) = b {
272-
bounds_span.push(lt.ident.span);
273-
}
274-
}
275-
}
276-
}
277-
if let Some(implementation_generics) = tcx.hir_get_generics(external_impl) {
278-
let mut impl_bounds = 0;
279-
for p in implementation_generics.predicates {
280-
if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind {
281-
for b in pred.bounds {
282-
if let hir::GenericBound::Outlives(_) = b {
283-
impl_bounds += 1;
284-
}
285-
}
286-
}
287-
}
288-
if impl_bounds == bounds_span.len() {
289-
bounds_span = vec![];
290-
} else if implementation_generics.has_where_clause_predicates {
291-
where_span = Some(implementation_generics.where_clause_span);
260+
let declaration_span = declaration
261+
.as_local()
262+
.and_then(|def_id| {
263+
let declaration_sig = get_declaration_sig(tcx, def_id).expect("foreign item sig");
264+
let pos = declaration_number_args.saturating_sub(1);
265+
declaration_sig.decl.inputs.get(pos).map(|arg| {
266+
if pos == 0 {
267+
arg.span
268+
} else {
269+
arg.span.with_lo(declaration_sig.decl.inputs[0].span.lo())
292270
}
271+
})
272+
})
273+
.or_else(|| tcx.hir_span_if_local(declaration))
274+
.unwrap_or_else(|| tcx.def_span(declaration));
275+
276+
let (_, external_impl_sig, _, _) = &tcx.hir_expect_item(external_impl).expect_fn();
277+
let pos = external_impl_number_args.saturating_sub(1);
278+
let impl_span = external_impl_sig
279+
.decl
280+
.inputs
281+
.get(pos)
282+
.map(|arg| {
283+
if pos == 0 {
284+
arg.span
285+
} else {
286+
arg.span.with_lo(external_impl_sig.decl.inputs[0].span.lo())
293287
}
294-
}
295-
let mut diag = tcx.dcx().create_err(LifetimesOrBoundsMismatchOnEii {
296-
span,
297-
ident: tcx.item_name(external_impl.to_def_id()),
298-
generics_span,
299-
bounds_span,
300-
where_span,
301-
});
288+
})
289+
.unwrap_or_else(|| tcx.def_span(external_impl));
302290

303-
diag.span_label(eii_attr_span, format!("required because of this attribute"));
304-
return Err(diag.emit());
305-
}
291+
let mut err = struct_span_code_err!(
292+
tcx.dcx(),
293+
impl_span,
294+
E0805,
295+
"`{external_impl_name}` has {} but #[{eii_name}] requires it to have {}",
296+
potentially_plural_count(external_impl_number_args, "parameter"),
297+
declaration_number_args
298+
);
306299

307-
Ok(())
300+
err.span_label(
301+
declaration_span,
302+
format!("requires {}", potentially_plural_count(declaration_number_args, "parameter")),
303+
);
304+
305+
err.span_label(
306+
impl_span,
307+
format!(
308+
"expected {}, found {}",
309+
potentially_plural_count(declaration_number_args, "parameter"),
310+
external_impl_number_args
311+
),
312+
);
313+
314+
err.span_label(eii_attr_span, format!("required because of this attribute"));
315+
316+
err.emit()
308317
}
309318

310319
fn report_eii_mismatch<'tcx>(

0 commit comments

Comments
 (0)