Add initial implementation of HIR-based WF checking for diagnostics
During well-formed checking, we walk through all types 'nested' in generic arguments. For example, WF-checking `Option<MyStruct<u8>>` will cause us to check `MyStruct<u8>` and `u8`. However, this is done on a `rustc_middle::ty::Ty`, which has no span information. As a result, any errors that occur will have a very general span (e.g. the definintion of an associated item). This becomes a problem when macros are involved. In general, an associated type like `type MyType = Option<MyStruct<u8>>;` may have completely different spans for each nested type in the HIR. Using the span of the entire associated item might end up pointing to a macro invocation, even though a user-provided span is available in one of the nested types. This PR adds a framework for HIR-based well formed checking. This check is only run during error reporting, and is used to obtain a more precise span for an existing error. This is accomplished by individually checking each 'nested' type in the HIR for the type, allowing us to find the most-specific type (and span) that produces a given error. The majority of the changes are to the error-reporting code. However, some of the general trait code is modified to pass through more information. Since this has no soundness implications, I've implemented a minimal version to begin with, which can be extended over time. In particular, this only works for HIR items with a corresponding `DefId` (e.g. it will not work for WF-checking performed within function bodies).
This commit is contained in:
parent
59d92bd017
commit
a765333738
23 changed files with 350 additions and 66 deletions
|
@ -1713,4 +1713,18 @@ rustc_queries! {
|
|||
query limits(key: ()) -> Limits {
|
||||
desc { "looking up limits" }
|
||||
}
|
||||
|
||||
/// Performs an HIR-based well-formed check on the item with the given `HirId`. If
|
||||
/// we get an `Umimplemented` error that matches the provided `Predicate`, return
|
||||
/// the cause of the newly created obligation.
|
||||
///
|
||||
/// This is only used by error-reporting code to get a better cause (in particular, a better
|
||||
/// span) for an *existing* error. Therefore, it is best-effort, and may never handle
|
||||
/// all of the cases that the normal `ty::Ty`-based wfcheck does. This is fine,
|
||||
/// because the `ty::Ty`-based wfcheck is always run.
|
||||
query diagnostic_hir_wf_check(key: (ty::Predicate<'tcx>, hir::HirId)) -> Option<traits::ObligationCause<'tcx>> {
|
||||
eval_always
|
||||
no_hash
|
||||
desc { "performing HIR wf-checking for predicate {:?} at item {:?}", key.0, key.1 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::mir::abstract_const::NotConstEvaluatable;
|
|||
use crate::ty::subst::SubstsRef;
|
||||
use crate::ty::{self, AdtKind, Ty, TyCtxt};
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
@ -24,7 +25,6 @@ use smallvec::SmallVec;
|
|||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache};
|
||||
|
||||
|
@ -87,7 +87,7 @@ pub enum Reveal {
|
|||
#[derive(Clone, PartialEq, Eq, Hash, Lift)]
|
||||
pub struct ObligationCause<'tcx> {
|
||||
/// `None` for `ObligationCause::dummy`, `Some` otherwise.
|
||||
data: Option<Rc<ObligationCauseData<'tcx>>>,
|
||||
data: Option<Lrc<ObligationCauseData<'tcx>>>,
|
||||
}
|
||||
|
||||
const DUMMY_OBLIGATION_CAUSE_DATA: ObligationCauseData<'static> =
|
||||
|
@ -131,7 +131,7 @@ impl<'tcx> ObligationCause<'tcx> {
|
|||
body_id: hir::HirId,
|
||||
code: ObligationCauseCode<'tcx>,
|
||||
) -> ObligationCause<'tcx> {
|
||||
ObligationCause { data: Some(Rc::new(ObligationCauseData { span, body_id, code })) }
|
||||
ObligationCause { data: Some(Lrc::new(ObligationCauseData { span, body_id, code })) }
|
||||
}
|
||||
|
||||
pub fn misc(span: Span, body_id: hir::HirId) -> ObligationCause<'tcx> {
|
||||
|
@ -148,7 +148,7 @@ impl<'tcx> ObligationCause<'tcx> {
|
|||
}
|
||||
|
||||
pub fn make_mut(&mut self) -> &mut ObligationCauseData<'tcx> {
|
||||
Rc::make_mut(self.data.get_or_insert_with(|| Rc::new(DUMMY_OBLIGATION_CAUSE_DATA)))
|
||||
Lrc::make_mut(self.data.get_or_insert_with(|| Lrc::new(DUMMY_OBLIGATION_CAUSE_DATA)))
|
||||
}
|
||||
|
||||
pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span {
|
||||
|
@ -326,6 +326,13 @@ pub enum ObligationCauseCode<'tcx> {
|
|||
|
||||
/// If `X` is the concrete type of an opaque type `impl Y`, then `X` must implement `Y`
|
||||
OpaqueType,
|
||||
|
||||
/// Well-formed checking. If a `HirId` is provided,
|
||||
/// it is used to perform HIR-based wf checking if an error
|
||||
/// occurs, in order to generate a more precise error message.
|
||||
/// This is purely for diagnostic purposes - it is always
|
||||
/// correct to use `MiscObligation` instead
|
||||
WellFormed(Option<hir::HirId>),
|
||||
}
|
||||
|
||||
impl ObligationCauseCode<'_> {
|
||||
|
@ -389,7 +396,7 @@ pub struct DerivedObligationCause<'tcx> {
|
|||
pub parent_trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
|
||||
/// The parent trait had this cause.
|
||||
pub parent_code: Rc<ObligationCauseCode<'tcx>>,
|
||||
pub parent_code: Lrc<ObligationCauseCode<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, TypeFoldable, Lift)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue