Skip to content

Commit db0324e

Browse files
committed
Support HIR wf checking for function signatures
During function type-checking, we normalize any associated types in the function signature (argument types + return type), and then create WF obligations for each of the normalized types. The HIR wf code does not currently support this case, so any errors that we get have imprecise spans. This commit extends `ObligationCauseCode::WellFormed` to support recording a function parameter, allowing us to get the corresponding HIR type if an error occurs. Function typechecking is modified to pass this information during signature normalization and WF checking. The resulting code is fairly verbose, due to the fact that we can no longer normalize the entire signature with a single function call. As part of the refactoring, we now perform HIR-based WF checking for several other 'typed items' (statics, consts, and inherent impls). As a result, WF and projection errors in a function signature now have a precise span, which points directly at the responsible type. If a function signature is constructed via a macro, this will allow the error message to point at the code 'most responsible' for the error (e.g. a user-supplied macro argument).
1 parent da7d405 commit db0324e

28 files changed

+240
-91
lines changed

compiler/rustc_middle/src/hir/map/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> {
2929
Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. })
3030
| Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. })
3131
| Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(&sig.decl),
32-
Node::Expr(Expr { kind: ExprKind::Closure(_, fn_decl, ..), .. }) => Some(fn_decl),
32+
Node::Expr(Expr { kind: ExprKind::Closure(_, fn_decl, ..), .. })
33+
| Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_decl, ..), .. }) => {
34+
Some(fn_decl)
35+
}
3336
_ => None,
3437
}
3538
}

compiler/rustc_middle/src/query/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1722,7 +1722,7 @@ rustc_queries! {
17221722
/// span) for an *existing* error. Therefore, it is best-effort, and may never handle
17231723
/// all of the cases that the normal `ty::Ty`-based wfcheck does. This is fine,
17241724
/// because the `ty::Ty`-based wfcheck is always run.
1725-
query diagnostic_hir_wf_check(key: (ty::Predicate<'tcx>, hir::HirId)) -> Option<traits::ObligationCause<'tcx>> {
1725+
query diagnostic_hir_wf_check(key: (ty::Predicate<'tcx>, traits::WellFormedLoc)) -> Option<traits::ObligationCause<'tcx>> {
17261726
eval_always
17271727
no_hash
17281728
desc { "performing HIR wf-checking for predicate {:?} at item {:?}", key.0, key.1 }

compiler/rustc_middle/src/traits/mod.rs

+28-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::ty::{self, AdtKind, Ty, TyCtxt};
1616
use rustc_data_structures::sync::Lrc;
1717
use rustc_errors::{Applicability, DiagnosticBuilder};
1818
use rustc_hir as hir;
19-
use rustc_hir::def_id::DefId;
19+
use rustc_hir::def_id::{DefId, LocalDefId};
2020
use rustc_hir::Constness;
2121
use rustc_span::symbol::Symbol;
2222
use rustc_span::{Span, DUMMY_SP};
@@ -327,17 +327,39 @@ pub enum ObligationCauseCode<'tcx> {
327327
/// If `X` is the concrete type of an opaque type `impl Y`, then `X` must implement `Y`
328328
OpaqueType,
329329

330-
/// Well-formed checking. If a `HirId` is provided,
331-
/// it is used to perform HIR-based wf checking if an error
332-
/// occurs, in order to generate a more precise error message.
330+
/// Well-formed checking. If a `WellFormedLoc` is provided,
331+
/// then it will be used to eprform HIR-based wf checking
332+
/// after an error occurs, in order to generate a more precise error span.
333333
/// This is purely for diagnostic purposes - it is always
334-
/// correct to use `MiscObligation` instead
335-
WellFormed(Option<hir::HirId>),
334+
/// correct to use `MiscObligation` instead, or to specify
335+
/// `WellFormed(None)`
336+
WellFormed(Option<WellFormedLoc>),
336337

337338
/// From `match_impl`. The cause for us having to match an impl, and the DefId we are matching against.
338339
MatchImpl(Lrc<ObligationCauseCode<'tcx>>, DefId),
339340
}
340341

342+
/// The 'location' at which we try to perform HIR-based wf checking.
343+
/// This information is used to obtain an `hir::Ty`, which
344+
/// we can walk in order to obtain precise spans for any
345+
/// 'nested' types (e.g. `Foo` in `Option<Foo>`).
346+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
347+
pub enum WellFormedLoc {
348+
/// Use the type of the provided definition.
349+
Ty(LocalDefId),
350+
/// Use the type of the parameter of the provided function.
351+
/// We cannot use `hir::Param`, since the function may
352+
/// not have a body (e.g. a trait method definition)
353+
Param {
354+
/// The function to lookup the parameter in
355+
function: LocalDefId,
356+
/// The index of the parameter to use.
357+
/// Parameters are indexed from 0, with the return type
358+
/// being the last 'parameter'
359+
param_idx: u16,
360+
},
361+
}
362+
341363
impl ObligationCauseCode<'_> {
342364
// Return the base obligation, ignoring derived obligations.
343365
pub fn peel_derives(&self) -> &Self {

compiler/rustc_middle/src/ty/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1682,7 +1682,7 @@ nop_list_lift! {bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariable
16821682
// This is the impl for `&'a InternalSubsts<'a>`.
16831683
nop_list_lift! {substs; GenericArg<'a> => GenericArg<'tcx>}
16841684

1685-
CloneLiftImpls! { for<'tcx> { Constness, } }
1685+
CloneLiftImpls! { for<'tcx> { Constness, traits::WellFormedLoc, } }
16861686

16871687
pub mod tls {
16881688
use super::{ptr_eq, GlobalCtxt, TyCtxt};

compiler/rustc_query_impl/src/keys.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! Defines the set of legal keys that can be used in queries.
22
33
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
4-
use rustc_hir::HirId;
54
use rustc_middle::infer::canonical::Canonical;
65
use rustc_middle::mir;
6+
use rustc_middle::traits;
77
use rustc_middle::ty::fast_reject::SimplifiedType;
88
use rustc_middle::ty::subst::{GenericArg, SubstsRef};
99
use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -397,7 +397,7 @@ impl<'tcx> Key for (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>) {
397397
}
398398
}
399399

400-
impl<'tcx> Key for (ty::Predicate<'tcx>, HirId) {
400+
impl<'tcx> Key for (ty::Predicate<'tcx>, traits::WellFormedLoc) {
401401
#[inline(always)]
402402
fn query_crate_is_local(&self) -> bool {
403403
true

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
242242
SelectionError::Unimplemented => {
243243
// If this obligation was generated as a result of well-formed checking, see if we
244244
// can get a better error message by performing HIR-based well formed checking.
245-
if let ObligationCauseCode::WellFormed(Some(wf_hir_id)) =
245+
if let ObligationCauseCode::WellFormed(Some(wf_loc)) =
246246
root_obligation.cause.code.peel_derives()
247247
{
248248
if let Some(cause) =
249-
self.tcx.diagnostic_hir_wf_check((obligation.predicate, *wf_hir_id))
249+
self.tcx.diagnostic_hir_wf_check((obligation.predicate, wf_loc.clone()))
250250
{
251251
obligation.cause = cause;
252252
span = obligation.cause.span;

compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use rustc_trait_selection::opaque_types::InferCtxtExt as _;
4040
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
4141
use rustc_trait_selection::traits::{
4242
self, ObligationCause, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt,
43+
WellFormedLoc,
4344
};
4445

4546
use std::collections::hash_map::Entry;
@@ -419,13 +420,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
419420
&self,
420421
span: Span,
421422
value: T,
422-
hir_id: hir::HirId,
423+
loc: WellFormedLoc,
423424
) -> T
424425
where
425426
T: TypeFoldable<'tcx>,
426427
{
427428
self.inh.normalize_associated_types_in_with_cause(
428-
ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(Some(hir_id))),
429+
ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(Some(loc))),
429430
self.param_env,
430431
value,
431432
)

compiler/rustc_typeck/src/check/wfcheck.rs

+62-10
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ use rustc_span::symbol::{sym, Ident, Symbol};
2222
use rustc_span::Span;
2323
use rustc_trait_selection::opaque_types::may_define_opaque_type;
2424
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
25-
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
25+
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, WellFormedLoc};
2626

27+
use std::convert::TryInto;
2728
use std::iter;
2829
use std::ops::ControlFlow;
2930

@@ -386,7 +387,7 @@ fn check_associated_item(
386387
span: Span,
387388
sig_if_method: Option<&hir::FnSig<'_>>,
388389
) {
389-
let code = ObligationCauseCode::WellFormed(Some(item_id));
390+
let code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(item_id.expect_owner())));
390391
for_id(tcx, item_id, span).with_fcx(|fcx| {
391392
let item = fcx.tcx.associated_item(fcx.tcx.hir().local_def_id(item_id));
392393

@@ -400,7 +401,11 @@ fn check_associated_item(
400401
match item.kind {
401402
ty::AssocKind::Const => {
402403
let ty = fcx.tcx.type_of(item.def_id);
403-
let ty = fcx.normalize_associated_types_in_wf(span, ty, item_id);
404+
let ty = fcx.normalize_associated_types_in_wf(
405+
span,
406+
ty,
407+
WellFormedLoc::Ty(item_id.expect_owner()),
408+
);
404409
fcx.register_wf_obligation(ty.into(), span, code.clone());
405410
}
406411
ty::AssocKind::Fn => {
@@ -422,7 +427,11 @@ fn check_associated_item(
422427
}
423428
if item.defaultness.has_value() {
424429
let ty = fcx.tcx.type_of(item.def_id);
425-
let ty = fcx.normalize_associated_types_in_wf(span, ty, item_id);
430+
let ty = fcx.normalize_associated_types_in_wf(
431+
span,
432+
ty,
433+
WellFormedLoc::Ty(item_id.expect_owner()),
434+
);
426435
fcx.register_wf_obligation(ty.into(), span, code.clone());
427436
}
428437
}
@@ -621,7 +630,11 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo
621630

622631
for_id(tcx, item_id, ty_span).with_fcx(|fcx| {
623632
let ty = tcx.type_of(tcx.hir().local_def_id(item_id));
624-
let item_ty = fcx.normalize_associated_types_in_wf(ty_span, ty, item_id);
633+
let item_ty = fcx.normalize_associated_types_in_wf(
634+
ty_span,
635+
ty,
636+
WellFormedLoc::Ty(item_id.expect_owner()),
637+
);
625638

626639
let mut forbid_unsized = true;
627640
if allow_foreign_ty {
@@ -634,7 +647,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo
634647
fcx.register_wf_obligation(
635648
item_ty.into(),
636649
ty_span,
637-
ObligationCauseCode::WellFormed(Some(item_id)),
650+
ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(item_id.expect_owner()))),
638651
);
639652
if forbid_unsized {
640653
fcx.register_bound(
@@ -684,7 +697,9 @@ fn check_impl<'tcx>(
684697
fcx.register_wf_obligation(
685698
self_ty.into(),
686699
ast_self_ty.span,
687-
ObligationCauseCode::WellFormed(Some(item.hir_id())),
700+
ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(
701+
item.hir_id().expect_owner(),
702+
))),
688703
);
689704
}
690705
}
@@ -901,11 +916,48 @@ fn check_fn_or_method<'fcx, 'tcx>(
901916
implied_bounds: &mut Vec<Ty<'tcx>>,
902917
) {
903918
let sig = fcx.tcx.liberate_late_bound_regions(def_id, sig);
904-
let sig = fcx.normalize_associated_types_in(span, sig);
905919

906-
for (&input_ty, ty) in iter::zip(sig.inputs(), hir_decl.inputs) {
907-
fcx.register_wf_obligation(input_ty.into(), ty.span, ObligationCauseCode::WellFormed(None));
920+
// Normalize the input and output types one at a time, using a different
921+
// `WellFormedLoc` for each. We cannot call `normalize_associated_types`
922+
// on the entire `FnSig`, since this would use the same `WellFormedLoc`
923+
// for each type, preventing the HIR wf check from generating
924+
// a nice error message.
925+
let ty::FnSig { mut inputs_and_output, c_variadic, unsafety, abi } = sig;
926+
inputs_and_output =
927+
fcx.tcx.mk_type_list(inputs_and_output.iter().enumerate().map(|(i, ty)| {
928+
fcx.normalize_associated_types_in_wf(
929+
span,
930+
ty,
931+
WellFormedLoc::Param {
932+
function: def_id.expect_local(),
933+
// Note that the `param_idx` of the output type is
934+
// one greater than the index of the last input type.
935+
param_idx: i.try_into().unwrap(),
936+
},
937+
)
938+
}));
939+
// Manually call `normalize_assocaited_types_in` on the other types
940+
// in `FnSig`. This ensures that if the types of these fields
941+
// ever change to include projections, we will start normalizing
942+
// them automatically.
943+
let sig = ty::FnSig {
944+
inputs_and_output,
945+
c_variadic: fcx.normalize_associated_types_in(span, c_variadic),
946+
unsafety: fcx.normalize_associated_types_in(span, unsafety),
947+
abi: fcx.normalize_associated_types_in(span, abi),
948+
};
949+
950+
for (i, (&input_ty, ty)) in iter::zip(sig.inputs(), hir_decl.inputs).enumerate() {
951+
fcx.register_wf_obligation(
952+
input_ty.into(),
953+
ty.span,
954+
ObligationCauseCode::WellFormed(Some(WellFormedLoc::Param {
955+
function: def_id.expect_local(),
956+
param_idx: i.try_into().unwrap(),
957+
})),
958+
);
908959
}
960+
909961
implied_bounds.extend(sig.inputs());
910962

911963
fcx.register_wf_obligation(

0 commit comments

Comments
 (0)