Auto merge of #83898 - Aaron1011:feature/hir-wf, r=estebank
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:
commit
32c447e179
23 changed files with 350 additions and 66 deletions
|
@ -70,6 +70,10 @@ pub struct FulfillmentError<'tcx> {
|
||||||
/// obligation error caused by a call argument. When this is the case, we also signal that in
|
/// obligation error caused by a call argument. When this is the case, we also signal that in
|
||||||
/// this field to ensure accuracy of suggestions.
|
/// this field to ensure accuracy of suggestions.
|
||||||
pub points_at_arg_span: bool,
|
pub points_at_arg_span: bool,
|
||||||
|
/// Diagnostics only: the 'root' obligation which resulted in
|
||||||
|
/// the failure to process `obligation`. This is the obligation
|
||||||
|
/// that was initially passed to `register_predicate_obligation`
|
||||||
|
pub root_obligation: PredicateObligation<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -122,8 +126,9 @@ impl<'tcx> FulfillmentError<'tcx> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
obligation: PredicateObligation<'tcx>,
|
obligation: PredicateObligation<'tcx>,
|
||||||
code: FulfillmentErrorCode<'tcx>,
|
code: FulfillmentErrorCode<'tcx>,
|
||||||
|
root_obligation: PredicateObligation<'tcx>,
|
||||||
) -> FulfillmentError<'tcx> {
|
) -> FulfillmentError<'tcx> {
|
||||||
FulfillmentError { obligation, code, points_at_arg_span: false }
|
FulfillmentError { obligation, code, points_at_arg_span: false, root_obligation }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1713,4 +1713,18 @@ rustc_queries! {
|
||||||
query limits(key: ()) -> Limits {
|
query limits(key: ()) -> Limits {
|
||||||
desc { "looking up 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::subst::SubstsRef;
|
||||||
use crate::ty::{self, AdtKind, Ty, TyCtxt};
|
use crate::ty::{self, AdtKind, Ty, TyCtxt};
|
||||||
|
|
||||||
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
|
@ -24,7 +25,6 @@ use smallvec::SmallVec;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache};
|
pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache};
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ pub enum Reveal {
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Lift)]
|
#[derive(Clone, PartialEq, Eq, Hash, Lift)]
|
||||||
pub struct ObligationCause<'tcx> {
|
pub struct ObligationCause<'tcx> {
|
||||||
/// `None` for `ObligationCause::dummy`, `Some` otherwise.
|
/// `None` for `ObligationCause::dummy`, `Some` otherwise.
|
||||||
data: Option<Rc<ObligationCauseData<'tcx>>>,
|
data: Option<Lrc<ObligationCauseData<'tcx>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const DUMMY_OBLIGATION_CAUSE_DATA: ObligationCauseData<'static> =
|
const DUMMY_OBLIGATION_CAUSE_DATA: ObligationCauseData<'static> =
|
||||||
|
@ -131,7 +131,7 @@ impl<'tcx> ObligationCause<'tcx> {
|
||||||
body_id: hir::HirId,
|
body_id: hir::HirId,
|
||||||
code: ObligationCauseCode<'tcx>,
|
code: ObligationCauseCode<'tcx>,
|
||||||
) -> ObligationCause<'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> {
|
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> {
|
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 {
|
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`
|
/// If `X` is the concrete type of an opaque type `impl Y`, then `X` must implement `Y`
|
||||||
OpaqueType,
|
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<'_> {
|
impl ObligationCauseCode<'_> {
|
||||||
|
@ -389,7 +396,7 @@ pub struct DerivedObligationCause<'tcx> {
|
||||||
pub parent_trait_ref: ty::PolyTraitRef<'tcx>,
|
pub parent_trait_ref: ty::PolyTraitRef<'tcx>,
|
||||||
|
|
||||||
/// The parent trait had this cause.
|
/// The parent trait had this cause.
|
||||||
pub parent_code: Rc<ObligationCauseCode<'tcx>>,
|
pub parent_code: Lrc<ObligationCauseCode<'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, TypeFoldable, Lift)]
|
#[derive(Clone, Debug, TypeFoldable, Lift)]
|
||||||
|
|
|
@ -2070,24 +2070,26 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
debug!("check_rvalue: is_const_fn={:?}", is_const_fn);
|
debug!("check_rvalue: is_const_fn={:?}", is_const_fn);
|
||||||
|
|
||||||
let def_id = body.source.def_id().expect_local();
|
let def_id = body.source.def_id().expect_local();
|
||||||
self.infcx.report_selection_error(
|
let obligation = traits::Obligation::new(
|
||||||
&traits::Obligation::new(
|
ObligationCause::new(
|
||||||
ObligationCause::new(
|
span,
|
||||||
span,
|
self.tcx().hir().local_def_id_to_hir_id(def_id),
|
||||||
self.tcx().hir().local_def_id_to_hir_id(def_id),
|
traits::ObligationCauseCode::RepeatVec(is_const_fn),
|
||||||
traits::ObligationCauseCode::RepeatVec(is_const_fn),
|
|
||||||
),
|
|
||||||
self.param_env,
|
|
||||||
ty::Binder::dummy(ty::TraitRef::new(
|
|
||||||
self.tcx().require_lang_item(
|
|
||||||
LangItem::Copy,
|
|
||||||
Some(self.last_span),
|
|
||||||
),
|
|
||||||
tcx.mk_substs_trait(ty, &[]),
|
|
||||||
))
|
|
||||||
.without_const()
|
|
||||||
.to_predicate(self.tcx()),
|
|
||||||
),
|
),
|
||||||
|
self.param_env,
|
||||||
|
ty::Binder::dummy(ty::TraitRef::new(
|
||||||
|
self.tcx().require_lang_item(
|
||||||
|
LangItem::Copy,
|
||||||
|
Some(self.last_span),
|
||||||
|
),
|
||||||
|
tcx.mk_substs_trait(ty, &[]),
|
||||||
|
))
|
||||||
|
.without_const()
|
||||||
|
.to_predicate(self.tcx()),
|
||||||
|
);
|
||||||
|
self.infcx.report_selection_error(
|
||||||
|
obligation.clone(),
|
||||||
|
&obligation,
|
||||||
&traits::SelectionError::Unimplemented,
|
&traits::SelectionError::Unimplemented,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//! Defines the set of legal keys that can be used in queries.
|
//! Defines the set of legal keys that can be used in queries.
|
||||||
|
|
||||||
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
|
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
|
||||||
|
use rustc_hir::HirId;
|
||||||
use rustc_middle::infer::canonical::Canonical;
|
use rustc_middle::infer::canonical::Canonical;
|
||||||
use rustc_middle::mir;
|
use rustc_middle::mir;
|
||||||
use rustc_middle::ty::fast_reject::SimplifiedType;
|
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||||
|
@ -395,3 +396,14 @@ impl<'tcx> Key for (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>) {
|
||||||
DUMMY_SP
|
DUMMY_SP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Key for (ty::Predicate<'tcx>, HirId) {
|
||||||
|
#[inline(always)]
|
||||||
|
fn query_crate_is_local(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
|
||||||
|
DUMMY_SP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,8 +30,7 @@ pub trait InferCtxtExt<'tcx> {
|
||||||
|
|
||||||
fn partially_normalize_associated_types_in<T>(
|
fn partially_normalize_associated_types_in<T>(
|
||||||
&self,
|
&self,
|
||||||
span: Span,
|
cause: ObligationCause<'tcx>,
|
||||||
body_id: hir::HirId,
|
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
value: T,
|
value: T,
|
||||||
) -> InferOk<'tcx, T>
|
) -> InferOk<'tcx, T>
|
||||||
|
@ -79,8 +78,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
|
||||||
/// new obligations that must further be processed.
|
/// new obligations that must further be processed.
|
||||||
fn partially_normalize_associated_types_in<T>(
|
fn partially_normalize_associated_types_in<T>(
|
||||||
&self,
|
&self,
|
||||||
span: Span,
|
cause: ObligationCause<'tcx>,
|
||||||
body_id: hir::HirId,
|
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
value: T,
|
value: T,
|
||||||
) -> InferOk<'tcx, T>
|
) -> InferOk<'tcx, T>
|
||||||
|
@ -89,7 +87,6 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
|
||||||
{
|
{
|
||||||
debug!("partially_normalize_associated_types_in(value={:?})", value);
|
debug!("partially_normalize_associated_types_in(value={:?})", value);
|
||||||
let mut selcx = traits::SelectionContext::new(self);
|
let mut selcx = traits::SelectionContext::new(self);
|
||||||
let cause = ObligationCause::misc(span, body_id);
|
|
||||||
let traits::Normalized { value, obligations } =
|
let traits::Normalized { value, obligations } =
|
||||||
traits::normalize(&mut selcx, param_env, cause, value);
|
traits::normalize(&mut selcx, param_env, cause, value);
|
||||||
debug!(
|
debug!(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::infer::InferCtxtExt as _;
|
use crate::infer::InferCtxtExt as _;
|
||||||
use crate::traits::{self, PredicateObligation};
|
use crate::traits::{self, ObligationCause, PredicateObligation};
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_data_structures::vec_map::VecMap;
|
use rustc_data_structures::vec_map::VecMap;
|
||||||
|
@ -1049,8 +1049,11 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
|
||||||
item_bounds.iter().map(|(bound, _)| bound.subst(tcx, substs)).collect();
|
item_bounds.iter().map(|(bound, _)| bound.subst(tcx, substs)).collect();
|
||||||
|
|
||||||
let param_env = tcx.param_env(def_id);
|
let param_env = tcx.param_env(def_id);
|
||||||
let InferOk { value: bounds, obligations } =
|
let InferOk { value: bounds, obligations } = infcx.partially_normalize_associated_types_in(
|
||||||
infcx.partially_normalize_associated_types_in(span, self.body_id, param_env, bounds);
|
ObligationCause::misc(span, self.body_id),
|
||||||
|
param_env,
|
||||||
|
bounds,
|
||||||
|
);
|
||||||
self.obligations.extend(obligations);
|
self.obligations.extend(obligations);
|
||||||
|
|
||||||
debug!("instantiate_opaque_types: bounds={:?}", bounds);
|
debug!("instantiate_opaque_types: bounds={:?}", bounds);
|
||||||
|
|
|
@ -58,6 +58,9 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
|
||||||
obligation: obligation.clone(),
|
obligation: obligation.clone(),
|
||||||
code: FulfillmentErrorCode::CodeAmbiguity,
|
code: FulfillmentErrorCode::CodeAmbiguity,
|
||||||
points_at_arg_span: false,
|
points_at_arg_span: false,
|
||||||
|
// FIXME - does Chalk have a notation of 'root obligation'?
|
||||||
|
// This is just for diagnostics, so it's okay if this is wrong
|
||||||
|
root_obligation: obligation.clone(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Err(errors)
|
Err(errors)
|
||||||
|
@ -105,11 +108,14 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
|
||||||
),
|
),
|
||||||
|
|
||||||
Err(_err) => errors.push(FulfillmentError {
|
Err(_err) => errors.push(FulfillmentError {
|
||||||
obligation,
|
obligation: obligation.clone(),
|
||||||
code: FulfillmentErrorCode::CodeSelectionError(
|
code: FulfillmentErrorCode::CodeSelectionError(
|
||||||
SelectionError::Unimplemented,
|
SelectionError::Unimplemented,
|
||||||
),
|
),
|
||||||
points_at_arg_span: false,
|
points_at_arg_span: false,
|
||||||
|
// FIXME - does Chalk have a notation of 'root obligation'?
|
||||||
|
// This is just for diagnostics, so it's okay if this is wrong
|
||||||
|
root_obligation: obligation,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -119,11 +125,14 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(NoSolution) => errors.push(FulfillmentError {
|
Err(NoSolution) => errors.push(FulfillmentError {
|
||||||
obligation,
|
obligation: obligation.clone(),
|
||||||
code: FulfillmentErrorCode::CodeSelectionError(
|
code: FulfillmentErrorCode::CodeSelectionError(
|
||||||
SelectionError::Unimplemented,
|
SelectionError::Unimplemented,
|
||||||
),
|
),
|
||||||
points_at_arg_span: false,
|
points_at_arg_span: false,
|
||||||
|
// FIXME - does Chalk have a notation of 'root obligation'?
|
||||||
|
// This is just for diagnostics, so it's okay if this is wrong
|
||||||
|
root_obligation: obligation,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,9 +55,13 @@ pub trait InferCtxtExt<'tcx> {
|
||||||
|
|
||||||
fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !;
|
fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !;
|
||||||
|
|
||||||
|
/// The `root_obligation` parameter should be the `root_obligation` field
|
||||||
|
/// from a `FulfillmentError`. If no `FulfillmentError` is available,
|
||||||
|
/// then it should be the same as `obligation`.
|
||||||
fn report_selection_error(
|
fn report_selection_error(
|
||||||
&self,
|
&self,
|
||||||
obligation: &PredicateObligation<'tcx>,
|
obligation: PredicateObligation<'tcx>,
|
||||||
|
root_obligation: &PredicateObligation<'tcx>,
|
||||||
error: &SelectionError<'tcx>,
|
error: &SelectionError<'tcx>,
|
||||||
fallback_has_occurred: bool,
|
fallback_has_occurred: bool,
|
||||||
points_at_arg: bool,
|
points_at_arg: bool,
|
||||||
|
@ -225,16 +229,29 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
fn report_selection_error(
|
fn report_selection_error(
|
||||||
&self,
|
&self,
|
||||||
obligation: &PredicateObligation<'tcx>,
|
mut obligation: PredicateObligation<'tcx>,
|
||||||
|
root_obligation: &PredicateObligation<'tcx>,
|
||||||
error: &SelectionError<'tcx>,
|
error: &SelectionError<'tcx>,
|
||||||
fallback_has_occurred: bool,
|
fallback_has_occurred: bool,
|
||||||
points_at_arg: bool,
|
points_at_arg: bool,
|
||||||
) {
|
) {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let span = obligation.cause.span;
|
let mut span = obligation.cause.span;
|
||||||
|
|
||||||
let mut err = match *error {
|
let mut err = match *error {
|
||||||
SelectionError::Unimplemented => {
|
SelectionError::Unimplemented => {
|
||||||
|
// If this obligation was generated as a result of well-formed checking, see if we
|
||||||
|
// can get a better error message by performing HIR-based well formed checking.
|
||||||
|
if let ObligationCauseCode::WellFormed(Some(wf_hir_id)) =
|
||||||
|
root_obligation.cause.code.peel_derives()
|
||||||
|
{
|
||||||
|
if let Some(cause) =
|
||||||
|
self.tcx.diagnostic_hir_wf_check((obligation.predicate, *wf_hir_id))
|
||||||
|
{
|
||||||
|
obligation.cause = cause;
|
||||||
|
span = obligation.cause.span;
|
||||||
|
}
|
||||||
|
}
|
||||||
if let ObligationCauseCode::CompareImplMethodObligation {
|
if let ObligationCauseCode::CompareImplMethodObligation {
|
||||||
item_name,
|
item_name,
|
||||||
impl_item_def_id,
|
impl_item_def_id,
|
||||||
|
@ -279,7 +296,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let OnUnimplementedNote { message, label, note, enclosing_scope } =
|
let OnUnimplementedNote { message, label, note, enclosing_scope } =
|
||||||
self.on_unimplemented_note(trait_ref, obligation);
|
self.on_unimplemented_note(trait_ref, &obligation);
|
||||||
let have_alt_message = message.is_some() || label.is_some();
|
let have_alt_message = message.is_some() || label.is_some();
|
||||||
let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
|
let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
|
||||||
let is_unsize =
|
let is_unsize =
|
||||||
|
@ -338,7 +355,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(ret_span) = self.return_type_span(obligation) {
|
if let Some(ret_span) = self.return_type_span(&obligation) {
|
||||||
err.span_label(
|
err.span_label(
|
||||||
ret_span,
|
ret_span,
|
||||||
&format!(
|
&format!(
|
||||||
|
@ -368,7 +385,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
points_at_arg,
|
points_at_arg,
|
||||||
have_alt_message,
|
have_alt_message,
|
||||||
) {
|
) {
|
||||||
self.note_obligation_cause(&mut err, obligation);
|
self.note_obligation_cause(&mut err, &obligation);
|
||||||
err.emit();
|
err.emit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -821,7 +838,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.note_obligation_cause(&mut err, obligation);
|
self.note_obligation_cause(&mut err, &obligation);
|
||||||
self.point_at_returns_when_relevant(&mut err, &obligation);
|
self.point_at_returns_when_relevant(&mut err, &obligation);
|
||||||
|
|
||||||
err.emit();
|
err.emit();
|
||||||
|
@ -1168,7 +1185,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
match error.code {
|
match error.code {
|
||||||
FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
|
FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
|
||||||
self.report_selection_error(
|
self.report_selection_error(
|
||||||
&error.obligation,
|
error.obligation.clone(),
|
||||||
|
&error.root_obligation,
|
||||||
selection_error,
|
selection_error,
|
||||||
fallback_has_occurred,
|
fallback_has_occurred,
|
||||||
error.points_at_arg_span,
|
error.points_at_arg_span,
|
||||||
|
|
|
@ -1902,7 +1902,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
| ObligationCauseCode::ReturnNoExpression
|
| ObligationCauseCode::ReturnNoExpression
|
||||||
| ObligationCauseCode::UnifyReceiver(..)
|
| ObligationCauseCode::UnifyReceiver(..)
|
||||||
| ObligationCauseCode::OpaqueType
|
| ObligationCauseCode::OpaqueType
|
||||||
| ObligationCauseCode::MiscObligation => {}
|
| ObligationCauseCode::MiscObligation
|
||||||
|
| ObligationCauseCode::WellFormed(..) => {}
|
||||||
ObligationCauseCode::SliceOrArrayElem => {
|
ObligationCauseCode::SliceOrArrayElem => {
|
||||||
err.note("slice and array elements must have `Sized` type");
|
err.note("slice and array elements must have `Sized` type");
|
||||||
}
|
}
|
||||||
|
|
|
@ -717,6 +717,10 @@ fn substs_infer_vars<'a, 'tcx>(
|
||||||
fn to_fulfillment_error<'tcx>(
|
fn to_fulfillment_error<'tcx>(
|
||||||
error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>,
|
error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>,
|
||||||
) -> FulfillmentError<'tcx> {
|
) -> FulfillmentError<'tcx> {
|
||||||
let obligation = error.backtrace.into_iter().next().unwrap().obligation;
|
let mut iter = error.backtrace.into_iter();
|
||||||
FulfillmentError::new(obligation, error.error)
|
let obligation = iter.next().unwrap().obligation;
|
||||||
|
// The root obligation is the last item in the backtrace - if there's only
|
||||||
|
// one item, then it's the same as the main obligation
|
||||||
|
let root_obligation = iter.next_back().map_or_else(|| obligation.clone(), |e| e.obligation);
|
||||||
|
FulfillmentError::new(obligation, error.error, root_obligation)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ use crate::traits::error_reporting::InferCtxtExt;
|
||||||
use crate::traits::project::ProjectionCacheKeyExt;
|
use crate::traits::project::ProjectionCacheKeyExt;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||||
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_errors::ErrorReported;
|
use rustc_errors::ErrorReported;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
|
@ -48,7 +49,6 @@ use std::cell::{Cell, RefCell};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub use rustc_middle::traits::select::*;
|
pub use rustc_middle::traits::select::*;
|
||||||
|
|
||||||
|
@ -2168,7 +2168,7 @@ impl<'tcx> TraitObligationExt<'tcx> for TraitObligation<'tcx> {
|
||||||
// by using -Z verbose or just a CLI argument.
|
// by using -Z verbose or just a CLI argument.
|
||||||
let derived_cause = DerivedObligationCause {
|
let derived_cause = DerivedObligationCause {
|
||||||
parent_trait_ref: obligation.predicate.to_poly_trait_ref(),
|
parent_trait_ref: obligation.predicate.to_poly_trait_ref(),
|
||||||
parent_code: Rc::new(obligation.cause.code.clone()),
|
parent_code: Lrc::new(obligation.cause.code.clone()),
|
||||||
};
|
};
|
||||||
let derived_code = variant(derived_cause);
|
let derived_code = variant(derived_cause);
|
||||||
ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code)
|
ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::infer::InferCtxt;
|
use crate::infer::InferCtxt;
|
||||||
use crate::opaque_types::required_region_bounds;
|
use crate::opaque_types::required_region_bounds;
|
||||||
use crate::traits;
|
use crate::traits;
|
||||||
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::lang_items::LangItem;
|
use rustc_hir::lang_items::LangItem;
|
||||||
|
@ -9,7 +10,6 @@ use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstnes
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::rc::Rc;
|
|
||||||
/// Returns the set of obligations needed to make `arg` well-formed.
|
/// Returns the set of obligations needed to make `arg` well-formed.
|
||||||
/// If `arg` contains unresolved inference variables, this may include
|
/// If `arg` contains unresolved inference variables, this may include
|
||||||
/// further WF obligations. However, if `arg` IS an unresolved
|
/// further WF obligations. However, if `arg` IS an unresolved
|
||||||
|
@ -295,7 +295,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
||||||
if let Some(parent_trait_ref) = obligation.predicate.to_opt_poly_trait_ref() {
|
if let Some(parent_trait_ref) = obligation.predicate.to_opt_poly_trait_ref() {
|
||||||
let derived_cause = traits::DerivedObligationCause {
|
let derived_cause = traits::DerivedObligationCause {
|
||||||
parent_trait_ref: parent_trait_ref.value,
|
parent_trait_ref: parent_trait_ref.value,
|
||||||
parent_code: Rc::new(obligation.cause.code.clone()),
|
parent_code: Lrc::new(obligation.cause.code.clone()),
|
||||||
};
|
};
|
||||||
cause.make_mut().code =
|
cause.make_mut().code =
|
||||||
traits::ObligationCauseCode::DerivedObligation(derived_cause);
|
traits::ObligationCauseCode::DerivedObligation(derived_cause);
|
||||||
|
|
|
@ -67,8 +67,7 @@ impl AscribeUserTypeCx<'me, 'tcx> {
|
||||||
{
|
{
|
||||||
self.infcx
|
self.infcx
|
||||||
.partially_normalize_associated_types_in(
|
.partially_normalize_associated_types_in(
|
||||||
DUMMY_SP,
|
ObligationCause::misc(DUMMY_SP, hir::CRATE_HIR_ID),
|
||||||
hir::CRATE_HIR_ID,
|
|
||||||
self.param_env,
|
self.param_env,
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
|
|
|
@ -640,7 +640,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
||||||
|
|
||||||
// Object safety violations or miscellaneous.
|
// Object safety violations or miscellaneous.
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.report_selection_error(&obligation, &err, false, false);
|
self.report_selection_error(
|
||||||
|
obligation.clone(),
|
||||||
|
&obligation,
|
||||||
|
&err,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
// Treat this like an obligation and follow through
|
// Treat this like an obligation and follow through
|
||||||
// with the unsizing - the lack of a coercion should
|
// with the unsizing - the lack of a coercion should
|
||||||
// be silent, as it causes a type mismatch later.
|
// be silent, as it causes a type mismatch later.
|
||||||
|
|
|
@ -39,7 +39,7 @@ use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||||
use rustc_trait_selection::opaque_types::InferCtxtExt as _;
|
use rustc_trait_selection::opaque_types::InferCtxtExt as _;
|
||||||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
||||||
use rustc_trait_selection::traits::{
|
use rustc_trait_selection::traits::{
|
||||||
self, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt,
|
self, ObligationCause, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
|
@ -408,6 +408,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience method which tracks extra diagnostic information for normalization
|
||||||
|
/// that occurs as a result of WF checking. The `hir_id` is the `HirId` of the hir item
|
||||||
|
/// whose type is being wf-checked - this is used to construct a more precise span if
|
||||||
|
/// an error occurs.
|
||||||
|
///
|
||||||
|
/// It is never necessary to call this method - calling `normalize_associated_types_in` will
|
||||||
|
/// just result in a slightly worse diagnostic span, and will still be sound.
|
||||||
|
pub(in super::super) fn normalize_associated_types_in_wf<T>(
|
||||||
|
&self,
|
||||||
|
span: Span,
|
||||||
|
value: T,
|
||||||
|
hir_id: hir::HirId,
|
||||||
|
) -> T
|
||||||
|
where
|
||||||
|
T: TypeFoldable<'tcx>,
|
||||||
|
{
|
||||||
|
self.inh.normalize_associated_types_in_with_cause(
|
||||||
|
ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(Some(hir_id))),
|
||||||
|
self.param_env,
|
||||||
|
value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub(in super::super) fn normalize_associated_types_in<T>(&self, span: Span, value: T) -> T
|
pub(in super::super) fn normalize_associated_types_in<T>(&self, span: Span, value: T) -> T
|
||||||
where
|
where
|
||||||
T: TypeFoldable<'tcx>,
|
T: TypeFoldable<'tcx>,
|
||||||
|
@ -423,7 +446,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
where
|
where
|
||||||
T: TypeFoldable<'tcx>,
|
T: TypeFoldable<'tcx>,
|
||||||
{
|
{
|
||||||
self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value)
|
self.inh.partially_normalize_associated_types_in(
|
||||||
|
ObligationCause::misc(span, self.body_id),
|
||||||
|
self.param_env,
|
||||||
|
value,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn require_type_meets(
|
pub fn require_type_meets(
|
||||||
|
|
|
@ -13,7 +13,7 @@ use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt};
|
||||||
use rustc_span::{self, Span};
|
use rustc_span::{self, Span};
|
||||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||||
use rustc_trait_selection::opaque_types::OpaqueTypeDecl;
|
use rustc_trait_selection::opaque_types::OpaqueTypeDecl;
|
||||||
use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt};
|
use rustc_trait_selection::traits::{self, ObligationCause, TraitEngine, TraitEngineExt};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -162,7 +162,23 @@ impl Inherited<'a, 'tcx> {
|
||||||
where
|
where
|
||||||
T: TypeFoldable<'tcx>,
|
T: TypeFoldable<'tcx>,
|
||||||
{
|
{
|
||||||
let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value);
|
self.normalize_associated_types_in_with_cause(
|
||||||
|
ObligationCause::misc(span, body_id),
|
||||||
|
param_env,
|
||||||
|
value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn normalize_associated_types_in_with_cause<T>(
|
||||||
|
&self,
|
||||||
|
cause: ObligationCause<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
value: T,
|
||||||
|
) -> T
|
||||||
|
where
|
||||||
|
T: TypeFoldable<'tcx>,
|
||||||
|
{
|
||||||
|
let ok = self.partially_normalize_associated_types_in(cause, param_env, value);
|
||||||
self.register_infer_ok_obligations(ok)
|
self.register_infer_ok_obligations(ok)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,7 +387,7 @@ fn check_associated_item(
|
||||||
) {
|
) {
|
||||||
debug!("check_associated_item: {:?}", item_id);
|
debug!("check_associated_item: {:?}", item_id);
|
||||||
|
|
||||||
let code = ObligationCauseCode::MiscObligation;
|
let code = ObligationCauseCode::WellFormed(Some(item_id));
|
||||||
for_id(tcx, item_id, span).with_fcx(|fcx| {
|
for_id(tcx, item_id, span).with_fcx(|fcx| {
|
||||||
let item = fcx.tcx.associated_item(fcx.tcx.hir().local_def_id(item_id));
|
let item = fcx.tcx.associated_item(fcx.tcx.hir().local_def_id(item_id));
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ fn check_associated_item(
|
||||||
match item.kind {
|
match item.kind {
|
||||||
ty::AssocKind::Const => {
|
ty::AssocKind::Const => {
|
||||||
let ty = fcx.tcx.type_of(item.def_id);
|
let ty = fcx.tcx.type_of(item.def_id);
|
||||||
let ty = fcx.normalize_associated_types_in(span, ty);
|
let ty = fcx.normalize_associated_types_in_wf(span, ty, item_id);
|
||||||
fcx.register_wf_obligation(ty.into(), span, code.clone());
|
fcx.register_wf_obligation(ty.into(), span, code.clone());
|
||||||
}
|
}
|
||||||
ty::AssocKind::Fn => {
|
ty::AssocKind::Fn => {
|
||||||
|
@ -423,7 +423,7 @@ fn check_associated_item(
|
||||||
}
|
}
|
||||||
if item.defaultness.has_value() {
|
if item.defaultness.has_value() {
|
||||||
let ty = fcx.tcx.type_of(item.def_id);
|
let ty = fcx.tcx.type_of(item.def_id);
|
||||||
let ty = fcx.normalize_associated_types_in(span, ty);
|
let ty = fcx.normalize_associated_types_in_wf(span, ty, item_id);
|
||||||
fcx.register_wf_obligation(ty.into(), span, code.clone());
|
fcx.register_wf_obligation(ty.into(), span, code.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,7 +515,8 @@ fn check_type_defn<'tcx, F>(
|
||||||
fcx.register_wf_obligation(
|
fcx.register_wf_obligation(
|
||||||
field.ty.into(),
|
field.ty.into(),
|
||||||
field.span,
|
field.span,
|
||||||
ObligationCauseCode::MiscObligation,
|
// We don't have an HIR id for the field
|
||||||
|
ObligationCauseCode::WellFormed(None),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,7 +622,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo
|
||||||
|
|
||||||
for_id(tcx, item_id, ty_span).with_fcx(|fcx| {
|
for_id(tcx, item_id, ty_span).with_fcx(|fcx| {
|
||||||
let ty = tcx.type_of(tcx.hir().local_def_id(item_id));
|
let ty = tcx.type_of(tcx.hir().local_def_id(item_id));
|
||||||
let item_ty = fcx.normalize_associated_types_in(ty_span, ty);
|
let item_ty = fcx.normalize_associated_types_in_wf(ty_span, ty, item_id);
|
||||||
|
|
||||||
let mut forbid_unsized = true;
|
let mut forbid_unsized = true;
|
||||||
if allow_foreign_ty {
|
if allow_foreign_ty {
|
||||||
|
@ -631,7 +632,11 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fcx.register_wf_obligation(item_ty.into(), ty_span, ObligationCauseCode::MiscObligation);
|
fcx.register_wf_obligation(
|
||||||
|
item_ty.into(),
|
||||||
|
ty_span,
|
||||||
|
ObligationCauseCode::WellFormed(Some(item_id)),
|
||||||
|
);
|
||||||
if forbid_unsized {
|
if forbid_unsized {
|
||||||
fcx.register_bound(
|
fcx.register_bound(
|
||||||
item_ty,
|
item_ty,
|
||||||
|
@ -680,7 +685,7 @@ fn check_impl<'tcx>(
|
||||||
fcx.register_wf_obligation(
|
fcx.register_wf_obligation(
|
||||||
self_ty.into(),
|
self_ty.into(),
|
||||||
ast_self_ty.span,
|
ast_self_ty.span,
|
||||||
ObligationCauseCode::MiscObligation,
|
ObligationCauseCode::WellFormed(Some(item.hir_id())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -746,7 +751,7 @@ fn check_where_clauses<'tcx, 'fcx>(
|
||||||
fcx.register_wf_obligation(
|
fcx.register_wf_obligation(
|
||||||
default_ct.into(),
|
default_ct.into(),
|
||||||
tcx.def_span(param.def_id),
|
tcx.def_span(param.def_id),
|
||||||
ObligationCauseCode::MiscObligation,
|
ObligationCauseCode::WellFormed(None),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -900,7 +905,7 @@ fn check_fn_or_method<'fcx, 'tcx>(
|
||||||
let sig = fcx.normalize_associated_types_in(span, sig);
|
let sig = fcx.normalize_associated_types_in(span, sig);
|
||||||
|
|
||||||
for (&input_ty, ty) in iter::zip(sig.inputs(), hir_decl.inputs) {
|
for (&input_ty, ty) in iter::zip(sig.inputs(), hir_decl.inputs) {
|
||||||
fcx.register_wf_obligation(input_ty.into(), ty.span, ObligationCauseCode::MiscObligation);
|
fcx.register_wf_obligation(input_ty.into(), ty.span, ObligationCauseCode::WellFormed(None));
|
||||||
}
|
}
|
||||||
implied_bounds.extend(sig.inputs());
|
implied_bounds.extend(sig.inputs());
|
||||||
|
|
||||||
|
|
133
compiler/rustc_typeck/src/hir_wf_check.rs
Normal file
133
compiler/rustc_typeck/src/hir_wf_check.rs
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
use crate::collect::ItemCtxt;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||||
|
use rustc_hir::HirId;
|
||||||
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
|
use rustc_infer::traits::ObligationCause;
|
||||||
|
use rustc_infer::traits::TraitEngine;
|
||||||
|
use rustc_middle::ty::query::Providers;
|
||||||
|
use rustc_middle::ty::{self, ToPredicate, TyCtxt};
|
||||||
|
use rustc_trait_selection::traits;
|
||||||
|
|
||||||
|
pub fn provide(providers: &mut Providers) {
|
||||||
|
*providers = Providers { diagnostic_hir_wf_check, ..*providers };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ideally, this would be in `rustc_trait_selection`, but we
|
||||||
|
// need access to `ItemCtxt`
|
||||||
|
fn diagnostic_hir_wf_check<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
(predicate, hir_id): (ty::Predicate<'tcx>, HirId),
|
||||||
|
) -> Option<ObligationCause<'tcx>> {
|
||||||
|
let hir = tcx.hir();
|
||||||
|
// HIR wfcheck should only ever happen as part of improving an existing error
|
||||||
|
tcx.sess.delay_span_bug(hir.span(hir_id), "Performed HIR wfcheck without an existing error!");
|
||||||
|
|
||||||
|
// Currently, we only handle WF checking for items (e.g. associated items).
|
||||||
|
// It would be nice to extend this to handle wf checks inside functions.
|
||||||
|
let def_id = match tcx.hir().opt_local_def_id(hir_id) {
|
||||||
|
Some(def_id) => def_id,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME - figure out how we want to handle wf-checking for
|
||||||
|
// things inside a function body.
|
||||||
|
let icx = ItemCtxt::new(tcx, def_id.to_def_id());
|
||||||
|
|
||||||
|
// To perform HIR-based WF checking, we iterate over all HIR types
|
||||||
|
// that occur 'inside' the item we're checking. For example,
|
||||||
|
// given the type `Option<MyStruct<u8>>`, we will check
|
||||||
|
// `Option<MyStruct<u8>>`, `MyStruct<u8>`, and `u8`.
|
||||||
|
// For each type, we perform a well-formed check, and see if we get
|
||||||
|
// an erorr that matches our expected predicate. We keep save
|
||||||
|
// the `ObligationCause` corresponding to the *innermost* type,
|
||||||
|
// which is the most specific type that we can point to.
|
||||||
|
// In general, the different components of an `hir::Ty` may have
|
||||||
|
// completely differentr spans due to macro invocations. Pointing
|
||||||
|
// to the most accurate part of the type can be the difference
|
||||||
|
// between a useless span (e.g. the macro invocation site)
|
||||||
|
// and a useful span (e.g. a user-provided type passed in to the macro).
|
||||||
|
//
|
||||||
|
// This approach is quite inefficient - we redo a lot of work done
|
||||||
|
// by the normal WF checker. However, this code is run at most once
|
||||||
|
// per reported error - it will have no impact when compilation succeeds,
|
||||||
|
// and should only have an impact if a very large number of errors are
|
||||||
|
// displaydd to the user.
|
||||||
|
struct HirWfCheck<'tcx> {
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
predicate: ty::Predicate<'tcx>,
|
||||||
|
cause: Option<ObligationCause<'tcx>>,
|
||||||
|
cause_depth: usize,
|
||||||
|
icx: ItemCtxt<'tcx>,
|
||||||
|
hir_id: HirId,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
|
||||||
|
type Map = intravisit::ErasedMap<'tcx>;
|
||||||
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||||
|
NestedVisitorMap::None
|
||||||
|
}
|
||||||
|
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
|
||||||
|
self.tcx.infer_ctxt().enter(|infcx| {
|
||||||
|
let mut fulfill = traits::FulfillmentContext::new();
|
||||||
|
let tcx_ty = self.icx.to_ty(ty);
|
||||||
|
let cause = traits::ObligationCause::new(
|
||||||
|
ty.span,
|
||||||
|
self.hir_id,
|
||||||
|
traits::ObligationCauseCode::MiscObligation,
|
||||||
|
);
|
||||||
|
fulfill.register_predicate_obligation(
|
||||||
|
&infcx,
|
||||||
|
traits::Obligation::new(
|
||||||
|
cause,
|
||||||
|
self.param_env,
|
||||||
|
ty::PredicateKind::WellFormed(tcx_ty.into()).to_predicate(self.tcx),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(errors) = fulfill.select_all_or_error(&infcx) {
|
||||||
|
tracing::debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
|
||||||
|
for error in errors {
|
||||||
|
if error.obligation.predicate == self.predicate {
|
||||||
|
// Save the cause from the greatest depth - this corresponds
|
||||||
|
// to picking more-specific types (e.g. `MyStruct<u8>`)
|
||||||
|
// over less-specific types (e.g. `Option<MyStruct<u8>>`)
|
||||||
|
if self.depth >= self.cause_depth {
|
||||||
|
self.cause = Some(error.obligation.cause);
|
||||||
|
self.cause_depth = self.depth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.depth += 1;
|
||||||
|
intravisit::walk_ty(self, ty);
|
||||||
|
self.depth -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut visitor = HirWfCheck {
|
||||||
|
tcx,
|
||||||
|
predicate,
|
||||||
|
cause: None,
|
||||||
|
cause_depth: 0,
|
||||||
|
icx,
|
||||||
|
hir_id,
|
||||||
|
param_env: tcx.param_env(def_id.to_def_id()),
|
||||||
|
depth: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let ty = match tcx.hir().get(hir_id) {
|
||||||
|
hir::Node::ImplItem(item) => match item.kind {
|
||||||
|
hir::ImplItemKind::TyAlias(ref ty) => Some(ty),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(ty) = ty {
|
||||||
|
visitor.visit_ty(ty);
|
||||||
|
}
|
||||||
|
visitor.cause
|
||||||
|
}
|
|
@ -88,6 +88,7 @@ mod coherence;
|
||||||
mod collect;
|
mod collect;
|
||||||
mod constrained_generic_params;
|
mod constrained_generic_params;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
pub mod hir_wf_check;
|
||||||
mod impl_wf_check;
|
mod impl_wf_check;
|
||||||
mod mem_categorization;
|
mod mem_categorization;
|
||||||
mod outlives;
|
mod outlives;
|
||||||
|
@ -462,6 +463,7 @@ pub fn provide(providers: &mut Providers) {
|
||||||
variance::provide(providers);
|
variance::provide(providers);
|
||||||
outlives::provide(providers);
|
outlives::provide(providers);
|
||||||
impl_wf_check::provide(providers);
|
impl_wf_check::provide(providers);
|
||||||
|
hir_wf_check::provide(providers);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorReported> {
|
pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorReported> {
|
||||||
|
|
12
src/test/ui/wf/wf-complex-assoc-type.rs
Normal file
12
src/test/ui/wf/wf-complex-assoc-type.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
trait MyTrait {}
|
||||||
|
struct AssertMyTrait<T: MyTrait>(T);
|
||||||
|
|
||||||
|
trait HelperTrait {
|
||||||
|
type MyItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HelperTrait for () {
|
||||||
|
type MyItem = Option<((AssertMyTrait<bool>, u8))>; //~ ERROR the trait bound
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
12
src/test/ui/wf/wf-complex-assoc-type.stderr
Normal file
12
src/test/ui/wf/wf-complex-assoc-type.stderr
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0277]: the trait bound `bool: MyTrait` is not satisfied
|
||||||
|
--> $DIR/wf-complex-assoc-type.rs:9:28
|
||||||
|
|
|
||||||
|
LL | struct AssertMyTrait<T: MyTrait>(T);
|
||||||
|
| ------- required by this bound in `AssertMyTrait`
|
||||||
|
...
|
||||||
|
LL | type MyItem = Option<((AssertMyTrait<bool>, u8))>;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^ the trait `MyTrait` is not implemented for `bool`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
|
@ -1,11 +1,11 @@
|
||||||
error[E0277]: the trait bound `T: MyHash` is not satisfied
|
error[E0277]: the trait bound `T: MyHash` is not satisfied
|
||||||
--> $DIR/wf-impl-associated-type-trait.rs:17:5
|
--> $DIR/wf-impl-associated-type-trait.rs:17:16
|
||||||
|
|
|
|
||||||
LL | pub struct MySet<T:MyHash> {
|
LL | pub struct MySet<T:MyHash> {
|
||||||
| ------ required by this bound in `MySet`
|
| ------ required by this bound in `MySet`
|
||||||
...
|
...
|
||||||
LL | type Bar = MySet<T>;
|
LL | type Bar = MySet<T>;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ the trait `MyHash` is not implemented for `T`
|
| ^^^^^^^^ the trait `MyHash` is not implemented for `T`
|
||||||
|
|
|
|
||||||
help: consider restricting type parameter `T`
|
help: consider restricting type parameter `T`
|
||||||
|
|
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue