1
Fork 0

mv compiler to compiler/

This commit is contained in:
mark 2020-08-27 22:58:48 -05:00 committed by Vadim Petrochenkov
parent db534b3ac2
commit 9e5f7d5631
1686 changed files with 941 additions and 1051 deletions

View file

@ -0,0 +1,233 @@
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::{self, TraitEngine};
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_infer::infer::InferCtxt;
use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, WithConstness};
use rustc_middle::ty::{ToPredicate, TypeFoldable};
use rustc_session::DiagnosticMessageId;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
#[derive(Copy, Clone, Debug)]
pub enum AutoderefKind {
Builtin,
Overloaded,
}
struct AutoderefSnapshot<'tcx> {
at_start: bool,
reached_recursion_limit: bool,
steps: Vec<(Ty<'tcx>, AutoderefKind)>,
cur_ty: Ty<'tcx>,
obligations: Vec<traits::PredicateObligation<'tcx>>,
}
pub struct Autoderef<'a, 'tcx> {
// Meta infos:
infcx: &'a InferCtxt<'a, 'tcx>,
span: Span,
body_id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
// Current state:
state: AutoderefSnapshot<'tcx>,
// Configurations:
include_raw_pointers: bool,
silence_errors: bool,
}
impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
type Item = (Ty<'tcx>, usize);
fn next(&mut self) -> Option<Self::Item> {
let tcx = self.infcx.tcx;
debug!("autoderef: steps={:?}, cur_ty={:?}", self.state.steps, self.state.cur_ty);
if self.state.at_start {
self.state.at_start = false;
debug!("autoderef stage #0 is {:?}", self.state.cur_ty);
return Some((self.state.cur_ty, 0));
}
// If we have reached the recursion limit, error gracefully.
if !tcx.sess.recursion_limit().value_within_limit(self.state.steps.len()) {
if !self.silence_errors {
report_autoderef_recursion_limit_error(tcx, self.span, self.state.cur_ty);
}
self.state.reached_recursion_limit = true;
return None;
}
if self.state.cur_ty.is_ty_var() {
return None;
}
// Otherwise, deref if type is derefable:
let (kind, new_ty) =
if let Some(mt) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) {
(AutoderefKind::Builtin, mt.ty)
} else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
(AutoderefKind::Overloaded, ty)
} else {
return None;
};
if new_ty.references_error() {
return None;
}
self.state.steps.push((self.state.cur_ty, kind));
debug!(
"autoderef stage #{:?} is {:?} from {:?}",
self.step_count(),
new_ty,
(self.state.cur_ty, kind)
);
self.state.cur_ty = new_ty;
Some((self.state.cur_ty, self.step_count()))
}
}
impl<'a, 'tcx> Autoderef<'a, 'tcx> {
pub fn new(
infcx: &'a InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_id: hir::HirId,
span: Span,
base_ty: Ty<'tcx>,
) -> Autoderef<'a, 'tcx> {
Autoderef {
infcx,
span,
body_id,
param_env,
state: AutoderefSnapshot {
steps: vec![],
cur_ty: infcx.resolve_vars_if_possible(&base_ty),
obligations: vec![],
at_start: true,
reached_recursion_limit: false,
},
include_raw_pointers: false,
silence_errors: false,
}
}
fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
debug!("overloaded_deref_ty({:?})", ty);
let tcx = self.infcx.tcx;
// <ty as Deref>
let trait_ref = TraitRef {
def_id: tcx.lang_items().deref_trait()?,
substs: tcx.mk_substs_trait(ty, &[]),
};
let cause = traits::ObligationCause::misc(self.span, self.body_id);
let obligation = traits::Obligation::new(
cause.clone(),
self.param_env,
trait_ref.without_const().to_predicate(tcx),
);
if !self.infcx.predicate_may_hold(&obligation) {
debug!("overloaded_deref_ty: cannot match obligation");
return None;
}
let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
let normalized_ty = fulfillcx.normalize_projection_type(
&self.infcx,
self.param_env,
ty::ProjectionTy::from_ref_and_name(
tcx,
trait_ref,
Ident::with_dummy_span(sym::Target),
),
cause,
);
if let Err(e) = fulfillcx.select_where_possible(&self.infcx) {
// This shouldn't happen, except for evaluate/fulfill mismatches,
// but that's not a reason for an ICE (`predicate_may_hold` is conservative
// by design).
debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", e);
return None;
}
let obligations = fulfillcx.pending_obligations();
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
self.state.obligations.extend(obligations);
Some(self.infcx.resolve_vars_if_possible(&normalized_ty))
}
/// Returns the final type we ended up with, which may be an inference
/// variable (we will resolve it first, if we want).
pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> {
if resolve {
self.infcx.resolve_vars_if_possible(&self.state.cur_ty)
} else {
self.state.cur_ty
}
}
pub fn step_count(&self) -> usize {
self.state.steps.len()
}
pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> {
self.state.obligations
}
pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] {
&self.state.steps
}
pub fn span(&self) -> Span {
self.span
}
pub fn reached_recursion_limit(&self) -> bool {
self.state.reached_recursion_limit
}
/// also dereference through raw pointer types
/// e.g., assuming ptr_to_Foo is the type `*const Foo`
/// fcx.autoderef(span, ptr_to_Foo) => [*const Foo]
/// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo]
pub fn include_raw_pointers(mut self) -> Self {
self.include_raw_pointers = true;
self
}
pub fn silence_errors(mut self) -> Self {
self.silence_errors = true;
self
}
}
pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
// We've reached the recursion limit, error gracefully.
let suggested_limit = tcx.sess.recursion_limit() * 2;
let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty);
let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg);
let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
if fresh {
struct_span_err!(
tcx.sess,
span,
E0055,
"reached the recursion limit while auto-dereferencing `{:?}`",
ty
)
.span_label(span, "deref recursion limit reached")
.help(&format!(
"consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)",
suggested_limit, tcx.crate_name,
))
.emit();
}
}

View file

@ -0,0 +1,182 @@
use crate::traits::query::outlives_bounds::InferCtxtExt as _;
use crate::traits::{self, TraitEngine, TraitEngineExt};
use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::traits::ObligationCause;
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::infer::canonical::{Canonical, CanonicalizedQueryResponse, QueryResponse};
use rustc_middle::traits::query::Fallible;
use rustc_middle::ty::{self, Ty, TypeFoldable};
use rustc_span::{Span, DUMMY_SP};
use std::fmt::Debug;
pub use rustc_infer::infer::*;
pub trait InferCtxtExt<'tcx> {
fn type_is_copy_modulo_regions(
&self,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
span: Span,
) -> bool;
fn partially_normalize_associated_types_in<T>(
&self,
span: Span,
body_id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
value: &T,
) -> InferOk<'tcx, T>
where
T: TypeFoldable<'tcx>;
}
impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
fn type_is_copy_modulo_regions(
&self,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
span: Span,
) -> bool {
let ty = self.resolve_vars_if_possible(&ty);
if !(param_env, ty).needs_infer() {
return ty.is_copy_modulo_regions(self.tcx.at(span), param_env);
}
let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None);
// This can get called from typeck (by euv), and `moves_by_default`
// rightly refuses to work with inference variables, but
// moves_by_default has a cache, which we want to use in other
// cases.
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id, span)
}
/// Normalizes associated types in `value`, potentially returning
/// new obligations that must further be processed.
fn partially_normalize_associated_types_in<T>(
&self,
span: Span,
body_id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
value: &T,
) -> InferOk<'tcx, T>
where
T: TypeFoldable<'tcx>,
{
debug!("partially_normalize_associated_types_in(value={:?})", value);
let mut selcx = traits::SelectionContext::new(self);
let cause = ObligationCause::misc(span, body_id);
let traits::Normalized { value, obligations } =
traits::normalize(&mut selcx, param_env, cause, value);
debug!(
"partially_normalize_associated_types_in: result={:?} predicates={:?}",
value, obligations
);
InferOk { value, obligations }
}
}
pub trait InferCtxtBuilderExt<'tcx> {
fn enter_canonical_trait_query<K, R>(
&mut self,
canonical_key: &Canonical<'tcx, K>,
operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, R>>
where
K: TypeFoldable<'tcx>,
R: Debug + TypeFoldable<'tcx>,
Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>;
}
impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
/// The "main method" for a canonicalized trait query. Given the
/// canonical key `canonical_key`, this method will create a new
/// inference context, instantiate the key, and run your operation
/// `op`. The operation should yield up a result (of type `R`) as
/// well as a set of trait obligations that must be fully
/// satisfied. These obligations will be processed and the
/// canonical result created.
///
/// Returns `NoSolution` in the event of any error.
///
/// (It might be mildly nicer to implement this on `TyCtxt`, and
/// not `InferCtxtBuilder`, but that is a bit tricky right now.
/// In part because we would need a `for<'tcx>` sort of
/// bound for the closure and in part because it is convenient to
/// have `'tcx` be free on this function so that we can talk about
/// `K: TypeFoldable<'tcx>`.)
fn enter_canonical_trait_query<K, R>(
&mut self,
canonical_key: &Canonical<'tcx, K>,
operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, R>>
where
K: TypeFoldable<'tcx>,
R: Debug + TypeFoldable<'tcx>,
Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
{
self.enter_with_canonical(
DUMMY_SP,
canonical_key,
|ref infcx, key, canonical_inference_vars| {
let mut fulfill_cx = TraitEngine::new(infcx.tcx);
let value = operation(infcx, &mut *fulfill_cx, key)?;
infcx.make_canonicalized_query_response(
canonical_inference_vars,
value,
&mut *fulfill_cx,
)
},
)
}
}
pub trait OutlivesEnvironmentExt<'tcx> {
fn add_implied_bounds(
&mut self,
infcx: &InferCtxt<'a, 'tcx>,
fn_sig_tys: &[Ty<'tcx>],
body_id: hir::HirId,
span: Span,
);
}
impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> {
/// This method adds "implied bounds" into the outlives environment.
/// Implied bounds are outlives relationships that we can deduce
/// on the basis that certain types must be well-formed -- these are
/// either the types that appear in the function signature or else
/// the input types to an impl. For example, if you have a function
/// like
///
/// ```
/// fn foo<'a, 'b, T>(x: &'a &'b [T]) { }
/// ```
///
/// we can assume in the caller's body that `'b: 'a` and that `T:
/// 'b` (and hence, transitively, that `T: 'a`). This method would
/// add those assumptions into the outlives-environment.
///
/// Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs`
fn add_implied_bounds(
&mut self,
infcx: &InferCtxt<'a, 'tcx>,
fn_sig_tys: &[Ty<'tcx>],
body_id: hir::HirId,
span: Span,
) {
debug!("add_implied_bounds()");
for &ty in fn_sig_tys {
let ty = infcx.resolve_vars_if_possible(&ty);
debug!("add_implied_bounds: ty = {}", ty);
let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span);
self.add_outlives_bounds(Some(infcx), implied_bounds)
}
}
}

View file

@ -0,0 +1,34 @@
//! This crates defines the trait resolution method.
//!
//! - **Traits.** Trait resolution is implemented in the `traits` module.
//!
//! For more information about how rustc works, see the [rustc-dev-guide].
//!
//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
//!
//! # Note
//!
//! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(bool_to_option)]
#![feature(drain_filter)]
#![feature(in_band_lifetimes)]
#![feature(crate_visibility_modifier)]
#![feature(or_patterns)]
#![recursion_limit = "512"] // For rustdoc
#[macro_use]
extern crate rustc_macros;
#[cfg(target_arch = "x86_64")]
#[macro_use]
extern crate rustc_data_structures;
#[macro_use]
extern crate tracing;
#[macro_use]
extern crate rustc_middle;
pub mod autoderef;
pub mod infer;
pub mod opaque_types;
pub mod traits;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,881 @@
//! Support code for rustdoc and external tools.
//! You really don't want to be using this unless you need to.
use super::*;
use crate::infer::region_constraints::{Constraint, RegionConstraintData};
use crate::infer::InferCtxt;
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::{Region, RegionVid};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use std::collections::hash_map::Entry;
use std::collections::VecDeque;
// FIXME(twk): this is obviously not nice to duplicate like that
#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
pub enum RegionTarget<'tcx> {
Region(Region<'tcx>),
RegionVid(RegionVid),
}
#[derive(Default, Debug, Clone)]
pub struct RegionDeps<'tcx> {
larger: FxHashSet<RegionTarget<'tcx>>,
smaller: FxHashSet<RegionTarget<'tcx>>,
}
pub enum AutoTraitResult<A> {
ExplicitImpl,
PositiveImpl(A),
NegativeImpl,
}
impl<A> AutoTraitResult<A> {
fn is_auto(&self) -> bool {
match *self {
AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl => true,
_ => false,
}
}
}
pub struct AutoTraitInfo<'cx> {
pub full_user_env: ty::ParamEnv<'cx>,
pub region_data: RegionConstraintData<'cx>,
pub vid_to_region: FxHashMap<ty::RegionVid, ty::Region<'cx>>,
}
pub struct AutoTraitFinder<'tcx> {
tcx: TyCtxt<'tcx>,
}
impl<'tcx> AutoTraitFinder<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>) -> Self {
AutoTraitFinder { tcx }
}
/// Makes a best effort to determine whether and under which conditions an auto trait is
/// implemented for a type. For example, if you have
///
/// ```
/// struct Foo<T> { data: Box<T> }
/// ```
///
/// then this might return that Foo<T>: Send if T: Send (encoded in the AutoTraitResult type).
/// The analysis attempts to account for custom impls as well as other complex cases. This
/// result is intended for use by rustdoc and other such consumers.
///
/// (Note that due to the coinductive nature of Send, the full and correct result is actually
/// quite simple to generate. That is, when a type has no custom impl, it is Send iff its field
/// types are all Send. So, in our example, we might have that Foo<T>: Send if Box<T>: Send.
/// But this is often not the best way to present to the user.)
///
/// Warning: The API should be considered highly unstable, and it may be refactored or removed
/// in the future.
pub fn find_auto_trait_generics<A>(
&self,
ty: Ty<'tcx>,
orig_env: ty::ParamEnv<'tcx>,
trait_did: DefId,
auto_trait_callback: impl Fn(&InferCtxt<'_, 'tcx>, AutoTraitInfo<'tcx>) -> A,
) -> AutoTraitResult<A> {
let tcx = self.tcx;
let trait_ref = ty::TraitRef { def_id: trait_did, substs: tcx.mk_substs_trait(ty, &[]) };
let trait_pred = ty::Binder::bind(trait_ref);
let bail_out = tcx.infer_ctxt().enter(|infcx| {
let mut selcx = SelectionContext::with_negative(&infcx, true);
let result = selcx.select(&Obligation::new(
ObligationCause::dummy(),
orig_env,
trait_pred.to_poly_trait_predicate(),
));
match result {
Ok(Some(ImplSource::ImplSourceUserDefined(_))) => {
debug!(
"find_auto_trait_generics({:?}): \
manual impl found, bailing out",
trait_ref
);
true
}
_ => false,
}
});
// If an explicit impl exists, it always takes priority over an auto impl
if bail_out {
return AutoTraitResult::ExplicitImpl;
}
tcx.infer_ctxt().enter(|infcx| {
let mut fresh_preds = FxHashSet::default();
// Due to the way projections are handled by SelectionContext, we need to run
// evaluate_predicates twice: once on the original param env, and once on the result of
// the first evaluate_predicates call.
//
// The problem is this: most of rustc, including SelectionContext and traits::project,
// are designed to work with a concrete usage of a type (e.g., Vec<u8>
// fn<T>() { Vec<T> }. This information will generally never change - given
// the 'T' in fn<T>() { ... }, we'll never know anything else about 'T'.
// If we're unable to prove that 'T' implements a particular trait, we're done -
// there's nothing left to do but error out.
//
// However, synthesizing an auto trait impl works differently. Here, we start out with
// a set of initial conditions - the ParamEnv of the struct/enum/union we're dealing
// with - and progressively discover the conditions we need to fulfill for it to
// implement a certain auto trait. This ends up breaking two assumptions made by trait
// selection and projection:
//
// * We can always cache the result of a particular trait selection for the lifetime of
// an InfCtxt
// * Given a projection bound such as '<T as SomeTrait>::SomeItem = K', if 'T:
// SomeTrait' doesn't hold, then we don't need to care about the 'SomeItem = K'
//
// We fix the first assumption by manually clearing out all of the InferCtxt's caches
// in between calls to SelectionContext.select. This allows us to keep all of the
// intermediate types we create bound to the 'tcx lifetime, rather than needing to lift
// them between calls.
//
// We fix the second assumption by reprocessing the result of our first call to
// evaluate_predicates. Using the example of '<T as SomeTrait>::SomeItem = K', our first
// pass will pick up 'T: SomeTrait', but not 'SomeItem = K'. On our second pass,
// traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing
// SelectionContext to return it back to us.
let (new_env, user_env) = match self.evaluate_predicates(
&infcx,
trait_did,
ty,
orig_env,
orig_env,
&mut fresh_preds,
false,
) {
Some(e) => e,
None => return AutoTraitResult::NegativeImpl,
};
let (full_env, full_user_env) = self
.evaluate_predicates(
&infcx,
trait_did,
ty,
new_env,
user_env,
&mut fresh_preds,
true,
)
.unwrap_or_else(|| {
panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env)
});
debug!(
"find_auto_trait_generics({:?}): fulfilling \
with {:?}",
trait_ref, full_env
);
infcx.clear_caches();
// At this point, we already have all of the bounds we need. FulfillmentContext is used
// to store all of the necessary region/lifetime bounds in the InferContext, as well as
// an additional sanity check.
let mut fulfill = FulfillmentContext::new();
fulfill.register_bound(&infcx, full_env, ty, trait_did, ObligationCause::dummy());
fulfill.select_all_or_error(&infcx).unwrap_or_else(|e| {
panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, e)
});
let body_id_map: FxHashMap<_, _> = infcx
.inner
.borrow()
.region_obligations()
.iter()
.map(|&(id, _)| (id, vec![]))
.collect();
infcx.process_registered_region_obligations(&body_id_map, None, full_env);
let region_data = infcx
.inner
.borrow_mut()
.unwrap_region_constraints()
.region_constraint_data()
.clone();
let vid_to_region = self.map_vid_to_region(&region_data);
let info = AutoTraitInfo { full_user_env, region_data, vid_to_region };
AutoTraitResult::PositiveImpl(auto_trait_callback(&infcx, info))
})
}
}
impl AutoTraitFinder<'tcx> {
/// The core logic responsible for computing the bounds for our synthesized impl.
///
/// To calculate the bounds, we call `SelectionContext.select` in a loop. Like
/// `FulfillmentContext`, we recursively select the nested obligations of predicates we
/// encounter. However, whenever we encounter an `UnimplementedError` involving a type
/// parameter, we add it to our `ParamEnv`. Since our goal is to determine when a particular
/// type implements an auto trait, Unimplemented errors tell us what conditions need to be met.
///
/// This method ends up working somewhat similarly to `FulfillmentContext`, but with a few key
/// differences. `FulfillmentContext` works under the assumption that it's dealing with concrete
/// user code. According, it considers all possible ways that a `Predicate` could be met, which
/// isn't always what we want for a synthesized impl. For example, given the predicate `T:
/// Iterator`, `FulfillmentContext` can end up reporting an Unimplemented error for `T:
/// IntoIterator` -- since there's an implementation of `Iterator` where `T: IntoIterator`,
/// `FulfillmentContext` will drive `SelectionContext` to consider that impl before giving up.
/// If we were to rely on `FulfillmentContext`s decision, we might end up synthesizing an impl
/// like this:
///
/// impl<T> Send for Foo<T> where T: IntoIterator
///
/// While it might be technically true that Foo implements Send where `T: IntoIterator`,
/// the bound is overly restrictive - it's really only necessary that `T: Iterator`.
///
/// For this reason, `evaluate_predicates` handles predicates with type variables specially.
/// When we encounter an `Unimplemented` error for a bound such as `T: Iterator`, we immediately
/// add it to our `ParamEnv`, and add it to our stack for recursive evaluation. When we later
/// select it, we'll pick up any nested bounds, without ever inferring that `T: IntoIterator`
/// needs to hold.
///
/// One additional consideration is supertrait bounds. Normally, a `ParamEnv` is only ever
/// constructed once for a given type. As part of the construction process, the `ParamEnv` will
/// have any supertrait bounds normalized -- e.g., if we have a type `struct Foo<T: Copy>`, the
/// `ParamEnv` will contain `T: Copy` and `T: Clone`, since `Copy: Clone`. When we construct our
/// own `ParamEnv`, we need to do this ourselves, through `traits::elaborate_predicates`, or
/// else `SelectionContext` will choke on the missing predicates. However, this should never
/// show up in the final synthesized generics: we don't want our generated docs page to contain
/// something like `T: Copy + Clone`, as that's redundant. Therefore, we keep track of a
/// separate `user_env`, which only holds the predicates that will actually be displayed to the
/// user.
fn evaluate_predicates(
&self,
infcx: &InferCtxt<'_, 'tcx>,
trait_did: DefId,
ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
user_env: ty::ParamEnv<'tcx>,
fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>,
only_projections: bool,
) -> Option<(ty::ParamEnv<'tcx>, ty::ParamEnv<'tcx>)> {
let tcx = infcx.tcx;
// Don't try to proess any nested obligations involving predicates
// that are already in the `ParamEnv` (modulo regions): we already
// know that they must hold.
for predicate in param_env.caller_bounds() {
fresh_preds.insert(self.clean_pred(infcx, predicate));
}
let mut select = SelectionContext::with_negative(&infcx, true);
let mut already_visited = FxHashSet::default();
let mut predicates = VecDeque::new();
predicates.push_back(ty::Binder::bind(ty::TraitPredicate {
trait_ref: ty::TraitRef {
def_id: trait_did,
substs: infcx.tcx.mk_substs_trait(ty, &[]),
},
}));
let computed_preds = param_env.caller_bounds().iter();
let mut user_computed_preds: FxHashSet<_> = user_env.caller_bounds().iter().collect();
let mut new_env = param_env;
let dummy_cause = ObligationCause::dummy();
while let Some(pred) = predicates.pop_front() {
infcx.clear_caches();
if !already_visited.insert(pred) {
continue;
}
// Call `infcx.resolve_vars_if_possible` to see if we can
// get rid of any inference variables.
let obligation = infcx.resolve_vars_if_possible(&Obligation::new(
dummy_cause.clone(),
new_env,
pred,
));
let result = select.select(&obligation);
match &result {
&Ok(Some(ref impl_source)) => {
// If we see an explicit negative impl (e.g., `impl !Send for MyStruct`),
// we immediately bail out, since it's impossible for us to continue.
if let ImplSource::ImplSourceUserDefined(ImplSourceUserDefinedData {
impl_def_id,
..
}) = impl_source
{
// Blame 'tidy' for the weird bracket placement.
if infcx.tcx.impl_polarity(*impl_def_id) == ty::ImplPolarity::Negative {
debug!(
"evaluate_nested_obligations: found explicit negative impl\
{:?}, bailing out",
impl_def_id
);
return None;
}
}
let obligations = impl_source.clone().nested_obligations().into_iter();
if !self.evaluate_nested_obligations(
ty,
obligations,
&mut user_computed_preds,
fresh_preds,
&mut predicates,
&mut select,
only_projections,
) {
return None;
}
}
&Ok(None) => {}
&Err(SelectionError::Unimplemented) => {
if self.is_param_no_infer(pred.skip_binder().trait_ref.substs) {
already_visited.remove(&pred);
self.add_user_pred(
&mut user_computed_preds,
pred.without_const().to_predicate(self.tcx),
);
predicates.push_back(pred);
} else {
debug!(
"evaluate_nested_obligations: `Unimplemented` found, bailing: \
{:?} {:?} {:?}",
ty,
pred,
pred.skip_binder().trait_ref.substs
);
return None;
}
}
_ => panic!("Unexpected error for '{:?}': {:?}", ty, result),
};
let normalized_preds = elaborate_predicates(
tcx,
computed_preds.clone().chain(user_computed_preds.iter().cloned()),
)
.map(|o| o.predicate);
new_env =
ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal(), None);
}
let final_user_env = ty::ParamEnv::new(
tcx.mk_predicates(user_computed_preds.into_iter()),
user_env.reveal(),
None,
);
debug!(
"evaluate_nested_obligations(ty={:?}, trait_did={:?}): succeeded with '{:?}' \
'{:?}'",
ty, trait_did, new_env, final_user_env
);
Some((new_env, final_user_env))
}
/// This method is designed to work around the following issue:
/// When we compute auto trait bounds, we repeatedly call `SelectionContext.select`,
/// progressively building a `ParamEnv` based on the results we get.
/// However, our usage of `SelectionContext` differs from its normal use within the compiler,
/// in that we capture and re-reprocess predicates from `Unimplemented` errors.
///
/// This can lead to a corner case when dealing with region parameters.
/// During our selection loop in `evaluate_predicates`, we might end up with
/// two trait predicates that differ only in their region parameters:
/// one containing a HRTB lifetime parameter, and one containing a 'normal'
/// lifetime parameter. For example:
///
/// T as MyTrait<'a>
/// T as MyTrait<'static>
///
/// If we put both of these predicates in our computed `ParamEnv`, we'll
/// confuse `SelectionContext`, since it will (correctly) view both as being applicable.
///
/// To solve this, we pick the 'more strict' lifetime bound -- i.e., the HRTB
/// Our end goal is to generate a user-visible description of the conditions
/// under which a type implements an auto trait. A trait predicate involving
/// a HRTB means that the type needs to work with any choice of lifetime,
/// not just one specific lifetime (e.g., `'static`).
fn add_user_pred(
&self,
user_computed_preds: &mut FxHashSet<ty::Predicate<'tcx>>,
new_pred: ty::Predicate<'tcx>,
) {
let mut should_add_new = true;
user_computed_preds.retain(|&old_pred| {
if let (
ty::PredicateAtom::Trait(new_trait, _),
ty::PredicateAtom::Trait(old_trait, _),
) = (new_pred.skip_binders(), old_pred.skip_binders())
{
if new_trait.def_id() == old_trait.def_id() {
let new_substs = new_trait.trait_ref.substs;
let old_substs = old_trait.trait_ref.substs;
if !new_substs.types().eq(old_substs.types()) {
// We can't compare lifetimes if the types are different,
// so skip checking `old_pred`.
return true;
}
for (new_region, old_region) in new_substs.regions().zip(old_substs.regions()) {
match (new_region, old_region) {
// If both predicates have an `ReLateBound` (a HRTB) in the
// same spot, we do nothing.
(
ty::RegionKind::ReLateBound(_, _),
ty::RegionKind::ReLateBound(_, _),
) => {}
(ty::RegionKind::ReLateBound(_, _), _)
| (_, ty::RegionKind::ReVar(_)) => {
// One of these is true:
// The new predicate has a HRTB in a spot where the old
// predicate does not (if they both had a HRTB, the previous
// match arm would have executed). A HRBT is a 'stricter'
// bound than anything else, so we want to keep the newer
// predicate (with the HRBT) in place of the old predicate.
//
// OR
//
// The old predicate has a region variable where the new
// predicate has some other kind of region. An region
// variable isn't something we can actually display to a user,
// so we choose their new predicate (which doesn't have a region
// variable).
//
// In both cases, we want to remove the old predicate,
// from `user_computed_preds`, and replace it with the new
// one. Having both the old and the new
// predicate in a `ParamEnv` would confuse `SelectionContext`.
//
// We're currently in the predicate passed to 'retain',
// so we return `false` to remove the old predicate from
// `user_computed_preds`.
return false;
}
(_, ty::RegionKind::ReLateBound(_, _))
| (ty::RegionKind::ReVar(_), _) => {
// This is the opposite situation as the previous arm.
// One of these is true:
//
// The old predicate has a HRTB lifetime in a place where the
// new predicate does not.
//
// OR
//
// The new predicate has a region variable where the old
// predicate has some other type of region.
//
// We want to leave the old
// predicate in `user_computed_preds`, and skip adding
// new_pred to `user_computed_params`.
should_add_new = false
}
_ => {}
}
}
}
}
true
});
if should_add_new {
user_computed_preds.insert(new_pred);
}
}
/// This is very similar to `handle_lifetimes`. However, instead of matching `ty::Region`s
/// to each other, we match `ty::RegionVid`s to `ty::Region`s.
fn map_vid_to_region<'cx>(
&self,
regions: &RegionConstraintData<'cx>,
) -> FxHashMap<ty::RegionVid, ty::Region<'cx>> {
let mut vid_map: FxHashMap<RegionTarget<'cx>, RegionDeps<'cx>> = FxHashMap::default();
let mut finished_map = FxHashMap::default();
for constraint in regions.constraints.keys() {
match constraint {
&Constraint::VarSubVar(r1, r2) => {
{
let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default();
deps1.larger.insert(RegionTarget::RegionVid(r2));
}
let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default();
deps2.smaller.insert(RegionTarget::RegionVid(r1));
}
&Constraint::RegSubVar(region, vid) => {
{
let deps1 = vid_map.entry(RegionTarget::Region(region)).or_default();
deps1.larger.insert(RegionTarget::RegionVid(vid));
}
let deps2 = vid_map.entry(RegionTarget::RegionVid(vid)).or_default();
deps2.smaller.insert(RegionTarget::Region(region));
}
&Constraint::VarSubReg(vid, region) => {
finished_map.insert(vid, region);
}
&Constraint::RegSubReg(r1, r2) => {
{
let deps1 = vid_map.entry(RegionTarget::Region(r1)).or_default();
deps1.larger.insert(RegionTarget::Region(r2));
}
let deps2 = vid_map.entry(RegionTarget::Region(r2)).or_default();
deps2.smaller.insert(RegionTarget::Region(r1));
}
}
}
while !vid_map.is_empty() {
let target = *vid_map.keys().next().expect("Keys somehow empty");
let deps = vid_map.remove(&target).expect("Entry somehow missing");
for smaller in deps.smaller.iter() {
for larger in deps.larger.iter() {
match (smaller, larger) {
(&RegionTarget::Region(_), &RegionTarget::Region(_)) => {
if let Entry::Occupied(v) = vid_map.entry(*smaller) {
let smaller_deps = v.into_mut();
smaller_deps.larger.insert(*larger);
smaller_deps.larger.remove(&target);
}
if let Entry::Occupied(v) = vid_map.entry(*larger) {
let larger_deps = v.into_mut();
larger_deps.smaller.insert(*smaller);
larger_deps.smaller.remove(&target);
}
}
(&RegionTarget::RegionVid(v1), &RegionTarget::Region(r1)) => {
finished_map.insert(v1, r1);
}
(&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => {
// Do nothing; we don't care about regions that are smaller than vids.
}
(&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => {
if let Entry::Occupied(v) = vid_map.entry(*smaller) {
let smaller_deps = v.into_mut();
smaller_deps.larger.insert(*larger);
smaller_deps.larger.remove(&target);
}
if let Entry::Occupied(v) = vid_map.entry(*larger) {
let larger_deps = v.into_mut();
larger_deps.smaller.insert(*smaller);
larger_deps.smaller.remove(&target);
}
}
}
}
}
}
finished_map
}
fn is_param_no_infer(&self, substs: SubstsRef<'_>) -> bool {
self.is_of_param(substs.type_at(0)) && !substs.types().any(|t| t.has_infer_types())
}
pub fn is_of_param(&self, ty: Ty<'_>) -> bool {
match ty.kind {
ty::Param(_) => true,
ty::Projection(p) => self.is_of_param(p.self_ty()),
_ => false,
}
}
fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool {
match p.ty().skip_binder().kind {
ty::Projection(proj) if proj == p.skip_binder().projection_ty => true,
_ => false,
}
}
fn evaluate_nested_obligations(
&self,
ty: Ty<'_>,
nested: impl Iterator<Item = Obligation<'tcx, ty::Predicate<'tcx>>>,
computed_preds: &mut FxHashSet<ty::Predicate<'tcx>>,
fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>,
predicates: &mut VecDeque<ty::PolyTraitPredicate<'tcx>>,
select: &mut SelectionContext<'_, 'tcx>,
only_projections: bool,
) -> bool {
let dummy_cause = ObligationCause::dummy();
for obligation in nested {
let is_new_pred =
fresh_preds.insert(self.clean_pred(select.infcx(), obligation.predicate));
// Resolve any inference variables that we can, to help selection succeed
let predicate = select.infcx().resolve_vars_if_possible(&obligation.predicate);
// We only add a predicate as a user-displayable bound if
// it involves a generic parameter, and doesn't contain
// any inference variables.
//
// Displaying a bound involving a concrete type (instead of a generic
// parameter) would be pointless, since it's always true
// (e.g. u8: Copy)
// Displaying an inference variable is impossible, since they're
// an internal compiler detail without a defined visual representation
//
// We check this by calling is_of_param on the relevant types
// from the various possible predicates
match predicate.skip_binders() {
ty::PredicateAtom::Trait(p, _) => {
if self.is_param_no_infer(p.trait_ref.substs)
&& !only_projections
&& is_new_pred
{
self.add_user_pred(computed_preds, predicate);
}
predicates.push_back(ty::Binder::bind(p));
}
ty::PredicateAtom::Projection(p) => {
let p = ty::Binder::bind(p);
debug!(
"evaluate_nested_obligations: examining projection predicate {:?}",
predicate
);
// As described above, we only want to display
// bounds which include a generic parameter but don't include
// an inference variable.
// Additionally, we check if we've seen this predicate before,
// to avoid rendering duplicate bounds to the user.
if self.is_param_no_infer(p.skip_binder().projection_ty.substs)
&& !p.ty().skip_binder().has_infer_types()
&& is_new_pred
{
debug!(
"evaluate_nested_obligations: adding projection predicate\
to computed_preds: {:?}",
predicate
);
// Under unusual circumstances, we can end up with a self-refeential
// projection predicate. For example:
// <T as MyType>::Value == <T as MyType>::Value
// Not only is displaying this to the user pointless,
// having it in the ParamEnv will cause an issue if we try to call
// poly_project_and_unify_type on the predicate, since this kind of
// predicate will normally never end up in a ParamEnv.
//
// For these reasons, we ignore these weird predicates,
// ensuring that we're able to properly synthesize an auto trait impl
if self.is_self_referential_projection(p) {
debug!(
"evaluate_nested_obligations: encountered a projection
predicate equating a type with itself! Skipping"
);
} else {
self.add_user_pred(computed_preds, predicate);
}
}
// There are three possible cases when we project a predicate:
//
// 1. We encounter an error. This means that it's impossible for
// our current type to implement the auto trait - there's bound
// that we could add to our ParamEnv that would 'fix' this kind
// of error, as it's not caused by an unimplemented type.
//
// 2. We successfully project the predicate (Ok(Some(_))), generating
// some subobligations. We then process these subobligations
// like any other generated sub-obligations.
//
// 3. We receive an 'ambiguous' result (Ok(None))
// If we were actually trying to compile a crate,
// we would need to re-process this obligation later.
// However, all we care about is finding out what bounds
// are needed for our type to implement a particular auto trait.
// We've already added this obligation to our computed ParamEnv
// above (if it was necessary). Therefore, we don't need
// to do any further processing of the obligation.
//
// Note that we *must* try to project *all* projection predicates
// we encounter, even ones without inference variable.
// This ensures that we detect any projection errors,
// which indicate that our type can *never* implement the given
// auto trait. In that case, we will generate an explicit negative
// impl (e.g. 'impl !Send for MyType'). However, we don't
// try to process any of the generated subobligations -
// they contain no new information, since we already know
// that our type implements the projected-through trait,
// and can lead to weird region issues.
//
// Normally, we'll generate a negative impl as a result of encountering
// a type with an explicit negative impl of an auto trait
// (for example, raw pointers have !Send and !Sync impls)
// However, through some **interesting** manipulations of the type
// system, it's actually possible to write a type that never
// implements an auto trait due to a projection error, not a normal
// negative impl error. To properly handle this case, we need
// to ensure that we catch any potential projection errors,
// and turn them into an explicit negative impl for our type.
debug!("Projecting and unifying projection predicate {:?}", predicate);
match project::poly_project_and_unify_type(select, &obligation.with(p)) {
Err(e) => {
debug!(
"evaluate_nested_obligations: Unable to unify predicate \
'{:?}' '{:?}', bailing out",
ty, e
);
return false;
}
Ok(Err(project::InProgress)) => {
debug!("evaluate_nested_obligations: recursive projection predicate");
return false;
}
Ok(Ok(Some(v))) => {
// We only care about sub-obligations
// when we started out trying to unify
// some inference variables. See the comment above
// for more infomration
if p.ty().skip_binder().has_infer_types() {
if !self.evaluate_nested_obligations(
ty,
v.into_iter(),
computed_preds,
fresh_preds,
predicates,
select,
only_projections,
) {
return false;
}
}
}
Ok(Ok(None)) => {
// It's ok not to make progress when have no inference variables -
// in that case, we were only performing unifcation to check if an
// error occurred (which would indicate that it's impossible for our
// type to implement the auto trait).
// However, we should always make progress (either by generating
// subobligations or getting an error) when we started off with
// inference variables
if p.ty().skip_binder().has_infer_types() {
panic!("Unexpected result when selecting {:?} {:?}", ty, obligation)
}
}
}
}
ty::PredicateAtom::RegionOutlives(binder) => {
let binder = ty::Binder::bind(binder);
if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() {
return false;
}
}
ty::PredicateAtom::TypeOutlives(binder) => {
let binder = ty::Binder::bind(binder);
match (
binder.no_bound_vars(),
binder.map_bound_ref(|pred| pred.0).no_bound_vars(),
) {
(None, Some(t_a)) => {
select.infcx().register_region_obligation_with_cause(
t_a,
select.infcx().tcx.lifetimes.re_static,
&dummy_cause,
);
}
(Some(ty::OutlivesPredicate(t_a, r_b)), _) => {
select.infcx().register_region_obligation_with_cause(
t_a,
r_b,
&dummy_cause,
);
}
_ => {}
};
}
ty::PredicateAtom::ConstEquate(c1, c2) => {
let evaluate = |c: &'tcx ty::Const<'tcx>| {
if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val {
match select.infcx().const_eval_resolve(
obligation.param_env,
def,
substs,
promoted,
Some(obligation.cause.span),
) {
Ok(val) => Ok(ty::Const::from_value(select.tcx(), val, c.ty)),
Err(err) => Err(err),
}
} else {
Ok(c)
}
};
match (evaluate(c1), evaluate(c2)) {
(Ok(c1), Ok(c2)) => {
match select
.infcx()
.at(&obligation.cause, obligation.param_env)
.eq(c1, c2)
{
Ok(_) => (),
Err(_) => return false,
}
}
_ => return false,
}
}
_ => panic!("Unexpected predicate {:?} {:?}", ty, predicate),
};
}
true
}
pub fn clean_pred(
&self,
infcx: &InferCtxt<'_, 'tcx>,
p: ty::Predicate<'tcx>,
) -> ty::Predicate<'tcx> {
infcx.freshen(p)
}
}
// Replaces all ReVars in a type with ty::Region's, using the provided map
pub struct RegionReplacer<'a, 'tcx> {
vid_to_region: &'a FxHashMap<ty::RegionVid, ty::Region<'tcx>>,
tcx: TyCtxt<'tcx>,
}
impl<'a, 'tcx> TypeFolder<'tcx> for RegionReplacer<'a, 'tcx> {
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
(match r {
&ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned(),
_ => None,
})
.unwrap_or_else(|| r.super_fold_with(self))
}
}

View file

@ -0,0 +1,267 @@
//! Defines a Chalk-based `TraitEngine`
use crate::infer::canonical::OriginalQueryValues;
use crate::infer::InferCtxt;
use crate::traits::query::NoSolution;
use crate::traits::{
ChalkEnvironmentAndGoal, ChalkEnvironmentClause, FulfillmentError, FulfillmentErrorCode,
ObligationCause, PredicateObligation, SelectionError, TraitEngine,
};
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, Ty, TyCtxt};
pub struct FulfillmentContext<'tcx> {
obligations: FxIndexSet<PredicateObligation<'tcx>>,
}
impl FulfillmentContext<'tcx> {
crate fn new() -> Self {
FulfillmentContext { obligations: FxIndexSet::default() }
}
}
fn environment<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
) -> &'tcx ty::List<ChalkEnvironmentClause<'tcx>> {
use rustc_hir::{ForeignItemKind, ImplItemKind, ItemKind, Node, TraitItemKind};
use rustc_middle::ty::subst::GenericArgKind;
debug!("environment(def_id = {:?})", def_id);
// The environment of an impl Trait type is its defining function's environment.
if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) {
return environment(tcx, parent);
}
// Compute the bounds on `Self` and the type parameters.
let ty::InstantiatedPredicates { predicates, .. } =
tcx.predicates_of(def_id).instantiate_identity(tcx);
let clauses = predicates.into_iter().map(ChalkEnvironmentClause::Predicate);
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
let node = tcx.hir().get(hir_id);
enum NodeKind {
TraitImpl,
InherentImpl,
Fn,
Other,
};
let node_kind = match node {
Node::TraitItem(item) => match item.kind {
TraitItemKind::Fn(..) => NodeKind::Fn,
_ => NodeKind::Other,
},
Node::ImplItem(item) => match item.kind {
ImplItemKind::Fn(..) => NodeKind::Fn,
_ => NodeKind::Other,
},
Node::Item(item) => match item.kind {
ItemKind::Impl { of_trait: Some(_), .. } => NodeKind::TraitImpl,
ItemKind::Impl { of_trait: None, .. } => NodeKind::InherentImpl,
ItemKind::Fn(..) => NodeKind::Fn,
_ => NodeKind::Other,
},
Node::ForeignItem(item) => match item.kind {
ForeignItemKind::Fn(..) => NodeKind::Fn,
_ => NodeKind::Other,
},
// FIXME: closures?
_ => NodeKind::Other,
};
// FIXME(eddyb) isn't the unordered nature of this a hazard?
let mut inputs = FxIndexSet::default();
match node_kind {
// In a trait impl, we assume that the header trait ref and all its
// constituents are well-formed.
NodeKind::TraitImpl => {
let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl");
// FIXME(chalk): this has problems because of late-bound regions
//inputs.extend(trait_ref.substs.iter().flat_map(|arg| arg.walk()));
inputs.extend(trait_ref.substs.iter());
}
// In an inherent impl, we assume that the receiver type and all its
// constituents are well-formed.
NodeKind::InherentImpl => {
let self_ty = tcx.type_of(def_id);
inputs.extend(self_ty.walk());
}
// In an fn, we assume that the arguments and all their constituents are
// well-formed.
NodeKind::Fn => {
let fn_sig = tcx.fn_sig(def_id);
let fn_sig = tcx.liberate_late_bound_regions(def_id, &fn_sig);
inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk()));
}
NodeKind::Other => (),
}
let input_clauses = inputs.into_iter().filter_map(|arg| {
match arg.unpack() {
GenericArgKind::Type(ty) => Some(ChalkEnvironmentClause::TypeFromEnv(ty)),
// FIXME(eddyb) no WF conditions from lifetimes?
GenericArgKind::Lifetime(_) => None,
// FIXME(eddyb) support const generics in Chalk
GenericArgKind::Const(_) => None,
}
});
tcx.mk_chalk_environment_clause_list(clauses.chain(input_clauses))
}
/// We need to wrap a `ty::Predicate` in an elaborated environment *before* we
/// canonicalize. This is due to the fact that we insert extra clauses into the
/// environment for all input types (`FromEnv`).
fn in_environment(
infcx: &InferCtxt<'_, 'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> ChalkEnvironmentAndGoal<'tcx> {
assert!(!infcx.is_in_snapshot());
let obligation = infcx.resolve_vars_if_possible(obligation);
let environment = match obligation.param_env.def_id {
Some(def_id) => environment(infcx.tcx, def_id),
None if obligation.param_env.caller_bounds().is_empty() => ty::List::empty(),
// FIXME(chalk): this is hit in ui/where-clauses/where-clause-constraints-are-local-for-trait-impl
// and ui/generics/generic-static-methods
//_ => bug!("non-empty `ParamEnv` with no def-id"),
_ => ty::List::empty(),
};
ChalkEnvironmentAndGoal { environment, goal: obligation.predicate }
}
impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
fn normalize_projection_type(
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
_param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
_cause: ObligationCause<'tcx>,
) -> Ty<'tcx> {
infcx.tcx.mk_ty(ty::Projection(projection_ty))
}
fn register_predicate_obligation(
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
obligation: PredicateObligation<'tcx>,
) {
assert!(!infcx.is_in_snapshot());
let obligation = infcx.resolve_vars_if_possible(&obligation);
self.obligations.insert(obligation);
}
fn select_all_or_error(
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
) -> Result<(), Vec<FulfillmentError<'tcx>>> {
self.select_where_possible(infcx)?;
if self.obligations.is_empty() {
Ok(())
} else {
let errors = self
.obligations
.iter()
.map(|obligation| FulfillmentError {
obligation: obligation.clone(),
code: FulfillmentErrorCode::CodeAmbiguity,
points_at_arg_span: false,
})
.collect();
Err(errors)
}
}
fn select_where_possible(
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
) -> Result<(), Vec<FulfillmentError<'tcx>>> {
let mut errors = Vec::new();
let mut next_round = FxIndexSet::default();
let mut making_progress;
loop {
making_progress = false;
// We iterate over all obligations, and record if we are able
// to unambiguously prove at least one obligation.
for obligation in self.obligations.drain(..) {
let goal_in_environment = in_environment(infcx, &obligation);
let mut orig_values = OriginalQueryValues::default();
let canonical_goal =
infcx.canonicalize_query(&goal_in_environment, &mut orig_values);
match infcx.tcx.evaluate_goal(canonical_goal) {
Ok(response) => {
if response.is_proven() {
making_progress = true;
match infcx.instantiate_query_response_and_region_obligations(
&obligation.cause,
obligation.param_env,
&orig_values,
&response,
) {
Ok(infer_ok) => next_round.extend(
infer_ok.obligations.into_iter().map(|obligation| {
assert!(!infcx.is_in_snapshot());
infcx.resolve_vars_if_possible(&obligation)
}),
),
Err(_err) => errors.push(FulfillmentError {
obligation,
code: FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
),
points_at_arg_span: false,
}),
}
} else {
// Ambiguous: retry at next round.
next_round.insert(obligation);
}
}
Err(NoSolution) => errors.push(FulfillmentError {
obligation,
code: FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
),
points_at_arg_span: false,
}),
}
}
next_round = std::mem::replace(&mut self.obligations, next_round);
if !making_progress {
break;
}
}
if errors.is_empty() { Ok(()) } else { Err(errors) }
}
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.obligations.iter().cloned().collect()
}
}

View file

@ -0,0 +1,126 @@
// This file contains various trait resolution methods used by codegen.
// They all assume regions can be erased and monomorphic types. It
// seems likely that they should eventually be merged into more
// general routines.
use crate::infer::{InferCtxt, TyCtxtInferExt};
use crate::traits::{
FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine,
Unimplemented,
};
use rustc_errors::ErrorReported;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{self, TyCtxt};
/// Attempts to resolve an obligation to a `ImplSource`. The result is
/// a shallow `ImplSource` resolution, meaning that we do not
/// (necessarily) resolve all nested obligations on the impl. Note
/// that type check should guarantee to us that all nested
/// obligations *could be* resolved if we wanted to.
/// Assumes that this is run after the entire crate has been successfully type-checked.
pub fn codegen_fulfill_obligation<'tcx>(
ty: TyCtxt<'tcx>,
(param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
) -> Result<ImplSource<'tcx, ()>, ErrorReported> {
// Remove any references to regions; this helps improve caching.
let trait_ref = ty.erase_regions(&trait_ref);
debug!(
"codegen_fulfill_obligation(trait_ref={:?}, def_id={:?})",
(param_env, trait_ref),
trait_ref.def_id()
);
// Do the initial selection for the obligation. This yields the
// shallow result we are looking for -- that is, what specific impl.
ty.infer_ctxt().enter(|infcx| {
let mut selcx = SelectionContext::new(&infcx);
let obligation_cause = ObligationCause::dummy();
let obligation =
Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate());
let selection = match selcx.select(&obligation) {
Ok(Some(selection)) => selection,
Ok(None) => {
// Ambiguity can happen when monomorphizing during trans
// expands to some humongo type that never occurred
// statically -- this humongo type can then overflow,
// leading to an ambiguous result. So report this as an
// overflow bug, since I believe this is the only case
// where ambiguity can result.
infcx.tcx.sess.delay_span_bug(
rustc_span::DUMMY_SP,
&format!(
"encountered ambiguity selecting `{:?}` during codegen, presuming due to \
overflow or prior type error",
trait_ref
),
);
return Err(ErrorReported);
}
Err(Unimplemented) => {
// This can trigger when we probe for the source of a `'static` lifetime requirement
// on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound.
infcx.tcx.sess.delay_span_bug(
rustc_span::DUMMY_SP,
&format!(
"Encountered error `Unimplemented` selecting `{:?}` during codegen",
trait_ref
),
);
return Err(ErrorReported);
}
Err(e) => {
bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref)
}
};
debug!("fulfill_obligation: selection={:?}", selection);
// Currently, we use a fulfillment context to completely resolve
// all nested obligations. This is because they can inform the
// inference of the impl's type parameters.
let mut fulfill_cx = FulfillmentContext::new();
let impl_source = selection.map(|predicate| {
debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate);
fulfill_cx.register_predicate_obligation(&infcx, predicate);
});
let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, &impl_source);
info!("Cache miss: {:?} => {:?}", trait_ref, impl_source);
Ok(impl_source)
})
}
// # Global Cache
/// Finishes processes any obligations that remain in the
/// fulfillment context, and then returns the result with all type
/// variables removed and regions erased. Because this is intended
/// for use after type-check has completed, if any errors occur,
/// it will panic. It is used during normalization and other cases
/// where processing the obligations in `fulfill_cx` may cause
/// type inference variables that appear in `result` to be
/// unified, and hence we need to process those obligations to get
/// the complete picture of the type.
fn drain_fulfillment_cx_or_panic<T>(
infcx: &InferCtxt<'_, 'tcx>,
fulfill_cx: &mut FulfillmentContext<'tcx>,
result: &T,
) -> T
where
T: TypeFoldable<'tcx>,
{
debug!("drain_fulfillment_cx_or_panic()");
// In principle, we only need to do this so long as `result`
// contains unbound type parameters. It could be a slight
// optimization to stop iterating early.
if let Err(errors) = fulfill_cx.select_all_or_error(infcx) {
bug!("Encountered errors `{:?}` resolving bounds after type-checking", errors);
}
let result = infcx.resolve_vars_if_possible(result);
infcx.tcx.erase_regions(&result)
}

View file

@ -0,0 +1,577 @@
//! See Rustc Dev Guide chapters on [trait-resolution] and [trait-specialization] for more info on
//! how this works.
//!
//! [trait-resolution]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
//! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
use crate::infer::{CombinedSnapshot, InferOk, TyCtxtInferExt};
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::SkipLeakCheck;
use crate::traits::{self, Normalized, Obligation, ObligationCause, SelectionContext};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::DUMMY_SP;
use std::iter;
/// Whether we do the orphan check relative to this crate or
/// to some remote crate.
#[derive(Copy, Clone, Debug)]
enum InCrate {
Local,
Remote,
}
#[derive(Debug, Copy, Clone)]
pub enum Conflict {
Upstream,
Downstream,
}
pub struct OverlapResult<'tcx> {
pub impl_header: ty::ImplHeader<'tcx>,
pub intercrate_ambiguity_causes: Vec<IntercrateAmbiguityCause>,
/// `true` if the overlap might've been permitted before the shift
/// to universes.
pub involves_placeholder: bool,
}
pub fn add_placeholder_note(err: &mut rustc_errors::DiagnosticBuilder<'_>) {
err.note(
"this behavior recently changed as a result of a bug fix; \
see rust-lang/rust#56105 for details",
);
}
/// If there are types that satisfy both impls, invokes `on_overlap`
/// with a suitably-freshened `ImplHeader` with those types
/// substituted. Otherwise, invokes `no_overlap`.
pub fn overlapping_impls<F1, F2, R>(
tcx: TyCtxt<'_>,
impl1_def_id: DefId,
impl2_def_id: DefId,
skip_leak_check: SkipLeakCheck,
on_overlap: F1,
no_overlap: F2,
) -> R
where
F1: FnOnce(OverlapResult<'_>) -> R,
F2: FnOnce() -> R,
{
debug!(
"overlapping_impls(\
impl1_def_id={:?}, \
impl2_def_id={:?})",
impl1_def_id, impl2_def_id,
);
let overlaps = tcx.infer_ctxt().enter(|infcx| {
let selcx = &mut SelectionContext::intercrate(&infcx);
overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).is_some()
});
if !overlaps {
return no_overlap();
}
// In the case where we detect an error, run the check again, but
// this time tracking intercrate ambuiguity causes for better
// diagnostics. (These take time and can lead to false errors.)
tcx.infer_ctxt().enter(|infcx| {
let selcx = &mut SelectionContext::intercrate(&infcx);
selcx.enable_tracking_intercrate_ambiguity_causes();
on_overlap(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).unwrap())
})
}
fn with_fresh_ty_vars<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
impl_def_id: DefId,
) -> ty::ImplHeader<'tcx> {
let tcx = selcx.tcx();
let impl_substs = selcx.infcx().fresh_substs_for_item(DUMMY_SP, impl_def_id);
let header = ty::ImplHeader {
impl_def_id,
self_ty: tcx.type_of(impl_def_id).subst(tcx, impl_substs),
trait_ref: tcx.impl_trait_ref(impl_def_id).subst(tcx, impl_substs),
predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates,
};
let Normalized { value: mut header, obligations } =
traits::normalize(selcx, param_env, ObligationCause::dummy(), &header);
header.predicates.extend(obligations.into_iter().map(|o| o.predicate));
header
}
/// Can both impl `a` and impl `b` be satisfied by a common type (including
/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
fn overlap<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
skip_leak_check: SkipLeakCheck,
a_def_id: DefId,
b_def_id: DefId,
) -> Option<OverlapResult<'tcx>> {
debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id);
selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
overlap_within_probe(selcx, skip_leak_check, a_def_id, b_def_id, snapshot)
})
}
fn overlap_within_probe(
selcx: &mut SelectionContext<'cx, 'tcx>,
skip_leak_check: SkipLeakCheck,
a_def_id: DefId,
b_def_id: DefId,
snapshot: &CombinedSnapshot<'_, 'tcx>,
) -> Option<OverlapResult<'tcx>> {
// For the purposes of this check, we don't bring any placeholder
// types into scope; instead, we replace the generic types with
// fresh type variables, and hence we do our evaluations in an
// empty environment.
let param_env = ty::ParamEnv::empty();
let a_impl_header = with_fresh_ty_vars(selcx, param_env, a_def_id);
let b_impl_header = with_fresh_ty_vars(selcx, param_env, b_def_id);
debug!("overlap: a_impl_header={:?}", a_impl_header);
debug!("overlap: b_impl_header={:?}", b_impl_header);
// Do `a` and `b` unify? If not, no overlap.
let obligations = match selcx
.infcx()
.at(&ObligationCause::dummy(), param_env)
.eq_impl_headers(&a_impl_header, &b_impl_header)
{
Ok(InferOk { obligations, value: () }) => obligations,
Err(_) => {
return None;
}
};
debug!("overlap: unification check succeeded");
// Are any of the obligations unsatisfiable? If so, no overlap.
let infcx = selcx.infcx();
let opt_failing_obligation = a_impl_header
.predicates
.iter()
.chain(&b_impl_header.predicates)
.map(|p| infcx.resolve_vars_if_possible(p))
.map(|p| Obligation {
cause: ObligationCause::dummy(),
param_env,
recursion_depth: 0,
predicate: p,
})
.chain(obligations)
.find(|o| !selcx.predicate_may_hold_fatal(o));
// FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported
// to the canonical trait query form, `infcx.predicate_may_hold`, once
// the new system supports intercrate mode (which coherence needs).
if let Some(failing_obligation) = opt_failing_obligation {
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
return None;
}
if !skip_leak_check.is_yes() {
if let Err(_) = infcx.leak_check(true, snapshot) {
debug!("overlap: leak check failed");
return None;
}
}
let impl_header = selcx.infcx().resolve_vars_if_possible(&a_impl_header);
let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes();
debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);
let involves_placeholder = match selcx.infcx().region_constraints_added_in_snapshot(snapshot) {
Some(true) => true,
_ => false,
};
Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
}
pub fn trait_ref_is_knowable<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
) -> Option<Conflict> {
debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref);
if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() {
// A downstream or cousin crate is allowed to implement some
// substitution of this trait-ref.
return Some(Conflict::Downstream);
}
if trait_ref_is_local_or_fundamental(tcx, trait_ref) {
// This is a local or fundamental trait, so future-compatibility
// is no concern. We know that downstream/cousin crates are not
// allowed to implement a substitution of this trait ref, which
// means impls could only come from dependencies of this crate,
// which we already know about.
return None;
}
// This is a remote non-fundamental trait, so if another crate
// can be the "final owner" of a substitution of this trait-ref,
// they are allowed to implement it future-compatibly.
//
// However, if we are a final owner, then nobody else can be,
// and if we are an intermediate owner, then we don't care
// about future-compatibility, which means that we're OK if
// we are an owner.
if orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok() {
debug!("trait_ref_is_knowable: orphan check passed");
None
} else {
debug!("trait_ref_is_knowable: nonlocal, nonfundamental, unowned");
Some(Conflict::Upstream)
}
}
pub fn trait_ref_is_local_or_fundamental<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
) -> bool {
trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental)
}
pub enum OrphanCheckErr<'tcx> {
NonLocalInputType(Vec<(Ty<'tcx>, bool /* Is this the first input type? */)>),
UncoveredTy(Ty<'tcx>, Option<Ty<'tcx>>),
}
/// Checks the coherence orphan rules. `impl_def_id` should be the
/// `DefId` of a trait impl. To pass, either the trait must be local, or else
/// two conditions must be satisfied:
///
/// 1. All type parameters in `Self` must be "covered" by some local type constructor.
/// 2. Some local type must appear in `Self`.
pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> {
debug!("orphan_check({:?})", impl_def_id);
// We only except this routine to be invoked on implementations
// of a trait, not inherent implementations.
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
debug!("orphan_check: trait_ref={:?}", trait_ref);
// If the *trait* is local to the crate, ok.
if trait_ref.def_id.is_local() {
debug!("trait {:?} is local to current crate", trait_ref.def_id);
return Ok(());
}
orphan_check_trait_ref(tcx, trait_ref, InCrate::Local)
}
/// Checks whether a trait-ref is potentially implementable by a crate.
///
/// The current rule is that a trait-ref orphan checks in a crate C:
///
/// 1. Order the parameters in the trait-ref in subst order - Self first,
/// others linearly (e.g., `<U as Foo<V, W>>` is U < V < W).
/// 2. Of these type parameters, there is at least one type parameter
/// in which, walking the type as a tree, you can reach a type local
/// to C where all types in-between are fundamental types. Call the
/// first such parameter the "local key parameter".
/// - e.g., `Box<LocalType>` is OK, because you can visit LocalType
/// going through `Box`, which is fundamental.
/// - similarly, `FundamentalPair<Vec<()>, Box<LocalType>>` is OK for
/// the same reason.
/// - but (knowing that `Vec<T>` is non-fundamental, and assuming it's
/// not local), `Vec<LocalType>` is bad, because `Vec<->` is between
/// the local type and the type parameter.
/// 3. Before this local type, no generic type parameter of the impl must
/// be reachable through fundamental types.
/// - e.g. `impl<T> Trait<LocalType> for Vec<T>` is fine, as `Vec` is not fundamental.
/// - while `impl<T> Trait<LocalType for Box<T>` results in an error, as `T` is
/// reachable through the fundamental type `Box`.
/// 4. Every type in the local key parameter not known in C, going
/// through the parameter's type tree, must appear only as a subtree of
/// a type local to C, with only fundamental types between the type
/// local to C and the local key parameter.
/// - e.g., `Vec<LocalType<T>>>` (or equivalently `Box<Vec<LocalType<T>>>`)
/// is bad, because the only local type with `T` as a subtree is
/// `LocalType<T>`, and `Vec<->` is between it and the type parameter.
/// - similarly, `FundamentalPair<LocalType<T>, T>` is bad, because
/// the second occurrence of `T` is not a subtree of *any* local type.
/// - however, `LocalType<Vec<T>>` is OK, because `T` is a subtree of
/// `LocalType<Vec<T>>`, which is local and has no types between it and
/// the type parameter.
///
/// The orphan rules actually serve several different purposes:
///
/// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where
/// every type local to one crate is unknown in the other) can't implement
/// the same trait-ref. This follows because it can be seen that no such
/// type can orphan-check in 2 such crates.
///
/// To check that a local impl follows the orphan rules, we check it in
/// InCrate::Local mode, using type parameters for the "generic" types.
///
/// 2. They ground negative reasoning for coherence. If a user wants to
/// write both a conditional blanket impl and a specific impl, we need to
/// make sure they do not overlap. For example, if we write
/// ```
/// impl<T> IntoIterator for Vec<T>
/// impl<T: Iterator> IntoIterator for T
/// ```
/// We need to be able to prove that `Vec<$0>: !Iterator` for every type $0.
/// We can observe that this holds in the current crate, but we need to make
/// sure this will also hold in all unknown crates (both "independent" crates,
/// which we need for link-safety, and also child crates, because we don't want
/// child crates to get error for impl conflicts in a *dependency*).
///
/// For that, we only allow negative reasoning if, for every assignment to the
/// inference variables, every unknown crate would get an orphan error if they
/// try to implement this trait-ref. To check for this, we use InCrate::Remote
/// mode. That is sound because we already know all the impls from known crates.
///
/// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can
/// add "non-blanket" impls without breaking negative reasoning in dependent
/// crates. This is the "rebalancing coherence" (RFC 1023) restriction.
///
/// For that, we only a allow crate to perform negative reasoning on
/// non-local-non-`#[fundamental]` only if there's a local key parameter as per (2).
///
/// Because we never perform negative reasoning generically (coherence does
/// not involve type parameters), this can be interpreted as doing the full
/// orphan check (using InCrate::Local mode), substituting non-local known
/// types for all inference variables.
///
/// This allows for crates to future-compatibly add impls as long as they
/// can't apply to types with a key parameter in a child crate - applying
/// the rules, this basically means that every type parameter in the impl
/// must appear behind a non-fundamental type (because this is not a
/// type-system requirement, crate owners might also go for "semantic
/// future-compatibility" involving things such as sealed traits, but
/// the above requirement is sufficient, and is necessary in "open world"
/// cases).
///
/// Note that this function is never called for types that have both type
/// parameters and inference variables.
fn orphan_check_trait_ref<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
in_crate: InCrate,
) -> Result<(), OrphanCheckErr<'tcx>> {
debug!("orphan_check_trait_ref(trait_ref={:?}, in_crate={:?})", trait_ref, in_crate);
if trait_ref.needs_infer() && trait_ref.needs_subst() {
bug!(
"can't orphan check a trait ref with both params and inference variables {:?}",
trait_ref
);
}
// Given impl<P1..=Pn> Trait<T1..=Tn> for T0, an impl is valid only
// if at least one of the following is true:
//
// - Trait is a local trait
// (already checked in orphan_check prior to calling this function)
// - All of
// - At least one of the types T0..=Tn must be a local type.
// Let Ti be the first such type.
// - No uncovered type parameters P1..=Pn may appear in T0..Ti (excluding Ti)
//
fn uncover_fundamental_ty<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
in_crate: InCrate,
) -> Vec<Ty<'tcx>> {
// FIXME: this is currently somewhat overly complicated,
// but fixing this requires a more complicated refactor.
if !contained_non_local_types(tcx, ty, in_crate).is_empty() {
if let Some(inner_tys) = fundamental_ty_inner_tys(tcx, ty) {
return inner_tys
.flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
.collect();
}
}
vec![ty]
}
let mut non_local_spans = vec![];
for (i, input_ty) in trait_ref
.substs
.types()
.flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
.enumerate()
{
debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty);
let non_local_tys = contained_non_local_types(tcx, input_ty, in_crate);
if non_local_tys.is_empty() {
debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty);
return Ok(());
} else if let ty::Param(_) = input_ty.kind {
debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty);
let local_type = trait_ref
.substs
.types()
.flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
.find(|ty| ty_is_local_constructor(ty, in_crate));
debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type);
return Err(OrphanCheckErr::UncoveredTy(input_ty, local_type));
}
for input_ty in non_local_tys {
non_local_spans.push((input_ty, i == 0));
}
}
// If we exit above loop, never found a local type.
debug!("orphan_check_trait_ref: no local type");
Err(OrphanCheckErr::NonLocalInputType(non_local_spans))
}
/// Returns a list of relevant non-local types for `ty`.
///
/// This is just `ty` itself unless `ty` is `#[fundamental]`,
/// in which case we recursively look into this type.
///
/// If `ty` is local itself, this method returns an empty `Vec`.
///
/// # Examples
///
/// - `u32` is not local, so this returns `[u32]`.
/// - for `Foo<u32>`, where `Foo` is a local type, this returns `[]`.
/// - `&mut u32` returns `[u32]`, as `&mut` is a fundamental type, similar to `Box`.
/// - `Box<Foo<u32>>` returns `[]`, as `Box` is a fundamental type and `Foo` is local.
fn contained_non_local_types(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, in_crate: InCrate) -> Vec<Ty<'tcx>> {
if ty_is_local_constructor(ty, in_crate) {
Vec::new()
} else {
match fundamental_ty_inner_tys(tcx, ty) {
Some(inner_tys) => {
inner_tys.flat_map(|ty| contained_non_local_types(tcx, ty, in_crate)).collect()
}
None => vec![ty],
}
}
}
/// For `#[fundamental]` ADTs and `&T` / `&mut T`, returns `Some` with the
/// type parameters of the ADT, or `T`, respectively. For non-fundamental
/// types, returns `None`.
fn fundamental_ty_inner_tys(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Option<impl Iterator<Item = Ty<'tcx>>> {
let (first_ty, rest_tys) = match ty.kind {
ty::Ref(_, ty, _) => (ty, ty::subst::InternalSubsts::empty().types()),
ty::Adt(def, substs) if def.is_fundamental() => {
let mut types = substs.types();
// FIXME(eddyb) actually validate `#[fundamental]` up-front.
match types.next() {
None => {
tcx.sess.span_err(
tcx.def_span(def.did),
"`#[fundamental]` requires at least one type parameter",
);
return None;
}
Some(first_ty) => (first_ty, types),
}
}
_ => return None,
};
Some(iter::once(first_ty).chain(rest_tys))
}
fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool {
match in_crate {
// The type is local to *this* crate - it will not be
// local in any other crate.
InCrate::Remote => false,
InCrate::Local => def_id.is_local(),
}
}
fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool {
debug!("ty_is_local_constructor({:?})", ty);
match ty.kind {
ty::Bool
| ty::Char
| ty::Int(..)
| ty::Uint(..)
| ty::Float(..)
| ty::Str
| ty::FnDef(..)
| ty::FnPtr(_)
| ty::Array(..)
| ty::Slice(..)
| ty::RawPtr(..)
| ty::Ref(..)
| ty::Never
| ty::Tuple(..)
| ty::Param(..)
| ty::Projection(..) => false,
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match in_crate {
InCrate::Local => false,
// The inference variable might be unified with a local
// type in that remote crate.
InCrate::Remote => true,
},
ty::Adt(def, _) => def_id_is_local(def.did, in_crate),
ty::Foreign(did) => def_id_is_local(did, in_crate),
ty::Opaque(..) => {
// This merits some explanation.
// Normally, opaque types are not involed when performing
// coherence checking, since it is illegal to directly
// implement a trait on an opaque type. However, we might
// end up looking at an opaque type during coherence checking
// if an opaque type gets used within another type (e.g. as
// a type parameter). This requires us to decide whether or
// not an opaque type should be considered 'local' or not.
//
// We choose to treat all opaque types as non-local, even
// those that appear within the same crate. This seems
// somewhat surprising at first, but makes sense when
// you consider that opaque types are supposed to hide
// the underlying type *within the same crate*. When an
// opaque type is used from outside the module
// where it is declared, it should be impossible to observe
// anything about it other than the traits that it implements.
//
// The alternative would be to look at the underlying type
// to determine whether or not the opaque type itself should
// be considered local. However, this could make it a breaking change
// to switch the underlying ('defining') type from a local type
// to a remote type. This would violate the rule that opaque
// types should be completely opaque apart from the traits
// that they implement, so we don't use this behavior.
false
}
ty::Dynamic(ref tt, ..) => {
if let Some(principal) = tt.principal() {
def_id_is_local(principal.def_id(), in_crate)
} else {
false
}
}
ty::Error(_) => true,
ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => {
bug!("ty_is_local invoked on unexpected type: {:?}", ty)
}
}
}

View file

@ -0,0 +1,18 @@
use rustc_middle::ty::TyCtxt;
use super::TraitEngine;
use super::{ChalkFulfillmentContext, FulfillmentContext};
pub trait TraitEngineExt<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> Box<Self>;
}
impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> Box<Self> {
if tcx.sess.opts.debugging_opts.chalk {
Box::new(ChalkFulfillmentContext::new())
} else {
Box::new(FulfillmentContext::new())
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,237 @@
use super::{
ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation,
};
use crate::infer::InferCtxt;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, GenericParamDefKind};
use rustc_span::symbol::sym;
use super::InferCtxtPrivExt;
crate trait InferCtxtExt<'tcx> {
/*private*/
fn impl_similar_to(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> Option<DefId>;
/*private*/
fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>;
fn on_unimplemented_note(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> OnUnimplementedNote;
}
impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
fn impl_similar_to(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> Option<DefId> {
let tcx = self.tcx;
let param_env = obligation.param_env;
let trait_ref = tcx.erase_late_bound_regions(&trait_ref);
let trait_self_ty = trait_ref.self_ty();
let mut self_match_impls = vec![];
let mut fuzzy_match_impls = vec![];
self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
let impl_self_ty = impl_trait_ref.self_ty();
if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) {
self_match_impls.push(def_id);
if trait_ref
.substs
.types()
.skip(1)
.zip(impl_trait_ref.substs.types().skip(1))
.all(|(u, v)| self.fuzzy_match_tys(u, v))
{
fuzzy_match_impls.push(def_id);
}
}
});
let impl_def_id = if self_match_impls.len() == 1 {
self_match_impls[0]
} else if fuzzy_match_impls.len() == 1 {
fuzzy_match_impls[0]
} else {
return None;
};
tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id)
}
/// Used to set on_unimplemented's `ItemContext`
/// to be the enclosing (async) block/function/closure
fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> {
let hir = &self.tcx.hir();
let node = hir.find(hir_id)?;
match &node {
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => {
self.describe_generator(*body_id).or_else(|| {
Some(match sig.header {
hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function",
_ => "a function",
})
})
}
hir::Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)),
..
}) => self.describe_generator(*body_id).or_else(|| Some("a trait method")),
hir::Node::ImplItem(hir::ImplItem {
kind: hir::ImplItemKind::Fn(sig, body_id),
..
}) => self.describe_generator(*body_id).or_else(|| {
Some(match sig.header {
hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method",
_ => "a method",
})
}),
hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability),
..
}) => self.describe_generator(*body_id).or_else(|| {
Some(if gen_movability.is_some() { "an async closure" } else { "a closure" })
}),
hir::Node::Expr(hir::Expr { .. }) => {
let parent_hid = hir.get_parent_node(hir_id);
if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None }
}
_ => None,
}
}
fn on_unimplemented_note(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> OnUnimplementedNote {
let def_id =
self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id());
let trait_ref = trait_ref.skip_binder();
let mut flags = vec![];
flags.push((
sym::ItemContext,
self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()),
));
match obligation.cause.code {
ObligationCauseCode::BuiltinDerivedObligation(..)
| ObligationCauseCode::ImplDerivedObligation(..)
| ObligationCauseCode::DerivedObligation(..) => {}
_ => {
// this is a "direct", user-specified, rather than derived,
// obligation.
flags.push((sym::direct, None));
}
}
if let ObligationCauseCode::ItemObligation(item)
| ObligationCauseCode::BindingObligation(item, _) = obligation.cause.code
{
// FIXME: maybe also have some way of handling methods
// from other traits? That would require name resolution,
// which we might want to be some sort of hygienic.
//
// Currently I'm leaving it for what I need for `try`.
if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
let method = self.tcx.item_name(item);
flags.push((sym::from_method, None));
flags.push((sym::from_method, Some(method.to_string())));
}
}
if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) {
flags.push((sym::parent_trait, Some(t)));
}
if let Some(k) = obligation.cause.span.desugaring_kind() {
flags.push((sym::from_desugaring, None));
flags.push((sym::from_desugaring, Some(format!("{:?}", k))));
}
let generics = self.tcx.generics_of(def_id);
let self_ty = trait_ref.self_ty();
// This is also included through the generics list as `Self`,
// but the parser won't allow you to use it
flags.push((sym::_Self, Some(self_ty.to_string())));
if let Some(def) = self_ty.ty_adt_def() {
// We also want to be able to select self's original
// signature with no type arguments resolved
flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string())));
}
for param in generics.params.iter() {
let value = match param.kind {
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => {
trait_ref.substs[param.index as usize].to_string()
}
GenericParamDefKind::Lifetime => continue,
};
let name = param.name;
flags.push((name, Some(value)));
}
if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) {
flags.push((sym::crate_local, None));
}
// Allow targeting all integers using `{integral}`, even if the exact type was resolved
if self_ty.is_integral() {
flags.push((sym::_Self, Some("{integral}".to_owned())));
}
if let ty::Array(aty, len) = self_ty.kind {
flags.push((sym::_Self, Some("[]".to_owned())));
flags.push((sym::_Self, Some(format!("[{}]", aty))));
if let Some(def) = aty.ty_adt_def() {
// We also want to be able to select the array's type's original
// signature with no type arguments resolved
flags.push((
sym::_Self,
Some(format!("[{}]", self.tcx.type_of(def.did).to_string())),
));
let tcx = self.tcx;
if let Some(len) = len.try_eval_usize(tcx, ty::ParamEnv::empty()) {
flags.push((
sym::_Self,
Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)),
));
} else {
flags.push((
sym::_Self,
Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())),
));
}
}
}
if let ty::Dynamic(traits, _) = self_ty.kind {
for t in traits.skip_binder() {
if let ty::ExistentialPredicate::Trait(trait_ref) = t {
flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
}
}
}
if let Ok(Some(command)) =
OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id)
{
command.evaluate(self.tcx, trait_ref, &flags[..])
} else {
OnUnimplementedNote::default()
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,668 @@
use crate::infer::{InferCtxt, TyOrConstInferVar};
use rustc_data_structures::obligation_forest::ProcessResult;
use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation};
use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
use rustc_errors::ErrorReported;
use rustc_infer::traits::{TraitEngine, TraitEngineExt as _, TraitObligation};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::{self, Binder, Const, Ty, TypeFoldable};
use std::marker::PhantomData;
use super::project;
use super::select::SelectionContext;
use super::wf;
use super::CodeAmbiguity;
use super::CodeProjectionError;
use super::CodeSelectionError;
use super::{ConstEvalFailure, Unimplemented};
use super::{FulfillmentError, FulfillmentErrorCode};
use super::{ObligationCause, PredicateObligation};
use crate::traits::error_reporting::InferCtxtExt as _;
use crate::traits::project::PolyProjectionObligation;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
/// Note that we include both the `ParamEnv` and the `Predicate`,
/// as the `ParamEnv` can influence whether fulfillment succeeds
/// or fails.
type CacheKey = ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>;
fn as_cache_key(&self) -> Self::CacheKey {
self.obligation.param_env.and(self.obligation.predicate)
}
}
/// The fulfillment context is used to drive trait resolution. It
/// consists of a list of obligations that must be (eventually)
/// satisfied. The job is to track which are satisfied, which yielded
/// errors, and which are still pending. At any point, users can call
/// `select_where_possible`, and the fulfillment context will try to do
/// selection, retaining only those obligations that remain
/// ambiguous. This may be helpful in pushing type inference
/// along. Once all type inference constraints have been generated, the
/// method `select_all_or_error` can be used to report any remaining
/// ambiguous cases as errors.
pub struct FulfillmentContext<'tcx> {
// A list of all obligations that have been registered with this
// fulfillment context.
predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
// Should this fulfillment context register type-lives-for-region
// obligations on its parent infcx? In some cases, region
// obligations are either already known to hold (normalization) or
// hopefully verifed elsewhere (type-impls-bound), and therefore
// should not be checked.
//
// Note that if we are normalizing a type that we already
// know is well-formed, there should be no harm setting this
// to true - all the region variables should be determinable
// using the RFC 447 rules, which don't depend on
// type-lives-for-region constraints, and because the type
// is well-formed, the constraints should hold.
register_region_obligations: bool,
// Is it OK to register obligations into this infcx inside
// an infcx snapshot?
//
// The "primary fulfillment" in many cases in typeck lives
// outside of any snapshot, so any use of it inside a snapshot
// will lead to trouble and therefore is checked against, but
// other fulfillment contexts sometimes do live inside of
// a snapshot (they don't *straddle* a snapshot, so there
// is no trouble there).
usable_in_snapshot: bool,
}
#[derive(Clone, Debug)]
pub struct PendingPredicateObligation<'tcx> {
pub obligation: PredicateObligation<'tcx>,
// This is far more often read than modified, meaning that we
// should mostly optimize for reading speed, while modifying is not as relevant.
//
// For whatever reason using a boxed slice is slower than using a `Vec` here.
pub stalled_on: Vec<TyOrConstInferVar<'tcx>>,
}
// `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger.
#[cfg(target_arch = "x86_64")]
static_assert_size!(PendingPredicateObligation<'_>, 64);
impl<'a, 'tcx> FulfillmentContext<'tcx> {
/// Creates a new fulfillment context.
pub fn new() -> FulfillmentContext<'tcx> {
FulfillmentContext {
predicates: ObligationForest::new(),
register_region_obligations: true,
usable_in_snapshot: false,
}
}
pub fn new_in_snapshot() -> FulfillmentContext<'tcx> {
FulfillmentContext {
predicates: ObligationForest::new(),
register_region_obligations: true,
usable_in_snapshot: true,
}
}
pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> {
FulfillmentContext {
predicates: ObligationForest::new(),
register_region_obligations: false,
usable_in_snapshot: false,
}
}
/// Attempts to select obligations using `selcx`.
fn select(
&mut self,
selcx: &mut SelectionContext<'a, 'tcx>,
) -> Result<(), Vec<FulfillmentError<'tcx>>> {
debug!("select(obligation-forest-size={})", self.predicates.len());
let mut errors = Vec::new();
loop {
debug!("select: starting another iteration");
// Process pending obligations.
let outcome = self.predicates.process_obligations(
&mut FulfillProcessor {
selcx,
register_region_obligations: self.register_region_obligations,
},
DoCompleted::No,
);
debug!("select: outcome={:#?}", outcome);
// FIXME: if we kept the original cache key, we could mark projection
// obligations as complete for the projection cache here.
errors.extend(outcome.errors.into_iter().map(to_fulfillment_error));
// If nothing new was added, no need to keep looping.
if outcome.stalled {
break;
}
}
debug!(
"select({} predicates remaining, {} errors) done",
self.predicates.len(),
errors.len()
);
if errors.is_empty() { Ok(()) } else { Err(errors) }
}
}
impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
/// "Normalize" a projection type `<SomeType as SomeTrait>::X` by
/// creating a fresh type variable `$0` as well as a projection
/// predicate `<SomeType as SomeTrait>::X == $0`. When the
/// inference engine runs, it will attempt to find an impl of
/// `SomeTrait` or a where-clause that lets us unify `$0` with
/// something concrete. If this fails, we'll unify `$0` with
/// `projection_ty` again.
fn normalize_projection_type(
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>,
) -> Ty<'tcx> {
debug!("normalize_projection_type(projection_ty={:?})", projection_ty);
debug_assert!(!projection_ty.has_escaping_bound_vars());
// FIXME(#20304) -- cache
let mut selcx = SelectionContext::new(infcx);
let mut obligations = vec![];
let normalized_ty = project::normalize_projection_type(
&mut selcx,
param_env,
projection_ty,
cause,
0,
&mut obligations,
);
self.register_predicate_obligations(infcx, obligations);
debug!("normalize_projection_type: result={:?}", normalized_ty);
normalized_ty
}
fn register_predicate_obligation(
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
obligation: PredicateObligation<'tcx>,
) {
// this helps to reduce duplicate errors, as well as making
// debug output much nicer to read and so on.
let obligation = infcx.resolve_vars_if_possible(&obligation);
debug!("register_predicate_obligation(obligation={:?})", obligation);
assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot);
self.predicates
.register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] });
}
fn select_all_or_error(
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
) -> Result<(), Vec<FulfillmentError<'tcx>>> {
self.select_where_possible(infcx)?;
let errors: Vec<_> = self
.predicates
.to_errors(CodeAmbiguity)
.into_iter()
.map(to_fulfillment_error)
.collect();
if errors.is_empty() { Ok(()) } else { Err(errors) }
}
fn select_where_possible(
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
) -> Result<(), Vec<FulfillmentError<'tcx>>> {
let mut selcx = SelectionContext::new(infcx);
self.select(&mut selcx)
}
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.predicates.map_pending_obligations(|o| o.obligation.clone())
}
}
struct FulfillProcessor<'a, 'b, 'tcx> {
selcx: &'a mut SelectionContext<'b, 'tcx>,
register_region_obligations: bool,
}
fn mk_pending(os: Vec<PredicateObligation<'tcx>>) -> Vec<PendingPredicateObligation<'tcx>> {
os.into_iter()
.map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] })
.collect()
}
impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
type Obligation = PendingPredicateObligation<'tcx>;
type Error = FulfillmentErrorCode<'tcx>;
/// Processes a predicate obligation and returns either:
/// - `Changed(v)` if the predicate is true, presuming that `v` are also true
/// - `Unchanged` if we don't have enough info to be sure
/// - `Error(e)` if the predicate does not hold
///
/// This is always inlined, despite its size, because it has a single
/// callsite and it is called *very* frequently.
#[inline(always)]
fn process_obligation(
&mut self,
pending_obligation: &mut Self::Obligation,
) -> ProcessResult<Self::Obligation, Self::Error> {
// If we were stalled on some unresolved variables, first check whether
// any of them have been resolved; if not, don't bother doing more work
// yet.
let change = match pending_obligation.stalled_on.len() {
// Match arms are in order of frequency, which matters because this
// code is so hot. 1 and 0 dominate; 2+ is fairly rare.
1 => {
let infer_var = pending_obligation.stalled_on[0];
self.selcx.infcx().ty_or_const_infer_var_changed(infer_var)
}
0 => {
// In this case we haven't changed, but wish to make a change.
true
}
_ => {
// This `for` loop was once a call to `all()`, but this lower-level
// form was a perf win. See #64545 for details.
(|| {
for &infer_var in &pending_obligation.stalled_on {
if self.selcx.infcx().ty_or_const_infer_var_changed(infer_var) {
return true;
}
}
false
})()
}
};
if !change {
debug!(
"process_predicate: pending obligation {:?} still stalled on {:?}",
self.selcx.infcx().resolve_vars_if_possible(&pending_obligation.obligation),
pending_obligation.stalled_on
);
return ProcessResult::Unchanged;
}
// This part of the code is much colder.
pending_obligation.stalled_on.truncate(0);
let obligation = &mut pending_obligation.obligation;
if obligation.predicate.has_infer_types_or_consts() {
obligation.predicate =
self.selcx.infcx().resolve_vars_if_possible(&obligation.predicate);
}
debug!("process_obligation: obligation = {:?} cause = {:?}", obligation, obligation.cause);
let infcx = self.selcx.infcx();
match obligation.predicate.kind() {
ty::PredicateKind::ForAll(binder) => match binder.skip_binder() {
// Evaluation will discard candidates using the leak check.
// This means we need to pass it the bound version of our
// predicate.
ty::PredicateAtom::Trait(trait_ref, _constness) => {
let trait_obligation = obligation.with(Binder::bind(trait_ref));
self.process_trait_obligation(
obligation,
trait_obligation,
&mut pending_obligation.stalled_on,
)
}
ty::PredicateAtom::Projection(data) => {
let project_obligation = obligation.with(Binder::bind(data));
self.process_projection_obligation(
project_obligation,
&mut pending_obligation.stalled_on,
)
}
ty::PredicateAtom::RegionOutlives(_)
| ty::PredicateAtom::TypeOutlives(_)
| ty::PredicateAtom::WellFormed(_)
| ty::PredicateAtom::ObjectSafe(_)
| ty::PredicateAtom::ClosureKind(..)
| ty::PredicateAtom::Subtype(_)
| ty::PredicateAtom::ConstEvaluatable(..)
| ty::PredicateAtom::ConstEquate(..) => {
let (pred, _) = infcx.replace_bound_vars_with_placeholders(binder);
ProcessResult::Changed(mk_pending(vec![
obligation.with(pred.to_predicate(self.selcx.tcx())),
]))
}
},
&ty::PredicateKind::Atom(atom) => match atom {
ty::PredicateAtom::Trait(ref data, _) => {
let trait_obligation = obligation.with(Binder::dummy(*data));
self.process_trait_obligation(
obligation,
trait_obligation,
&mut pending_obligation.stalled_on,
)
}
ty::PredicateAtom::RegionOutlives(data) => {
match infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)) {
Ok(()) => ProcessResult::Changed(vec![]),
Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)),
}
}
ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(t_a, r_b)) => {
if self.register_region_obligations {
self.selcx.infcx().register_region_obligation_with_cause(
t_a,
r_b,
&obligation.cause,
);
}
ProcessResult::Changed(vec![])
}
ty::PredicateAtom::Projection(ref data) => {
let project_obligation = obligation.with(Binder::dummy(*data));
self.process_projection_obligation(
project_obligation,
&mut pending_obligation.stalled_on,
)
}
ty::PredicateAtom::ObjectSafe(trait_def_id) => {
if !self.selcx.tcx().is_object_safe(trait_def_id) {
ProcessResult::Error(CodeSelectionError(Unimplemented))
} else {
ProcessResult::Changed(vec![])
}
}
ty::PredicateAtom::ClosureKind(_, closure_substs, kind) => {
match self.selcx.infcx().closure_kind(closure_substs) {
Some(closure_kind) => {
if closure_kind.extends(kind) {
ProcessResult::Changed(vec![])
} else {
ProcessResult::Error(CodeSelectionError(Unimplemented))
}
}
None => ProcessResult::Unchanged,
}
}
ty::PredicateAtom::WellFormed(arg) => {
match wf::obligations(
self.selcx.infcx(),
obligation.param_env,
obligation.cause.body_id,
arg,
obligation.cause.span,
) {
None => {
pending_obligation.stalled_on =
vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()];
ProcessResult::Unchanged
}
Some(os) => ProcessResult::Changed(mk_pending(os)),
}
}
ty::PredicateAtom::Subtype(subtype) => {
match self.selcx.infcx().subtype_predicate(
&obligation.cause,
obligation.param_env,
Binder::dummy(subtype),
) {
None => {
// None means that both are unresolved.
pending_obligation.stalled_on = vec![
TyOrConstInferVar::maybe_from_ty(subtype.a).unwrap(),
TyOrConstInferVar::maybe_from_ty(subtype.b).unwrap(),
];
ProcessResult::Unchanged
}
Some(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)),
Some(Err(err)) => {
let expected_found =
ExpectedFound::new(subtype.a_is_expected, subtype.a, subtype.b);
ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError(
expected_found,
err,
))
}
}
}
ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
match self.selcx.infcx().const_eval_resolve(
obligation.param_env,
def_id,
substs,
None,
Some(obligation.cause.span),
) {
Ok(_) => ProcessResult::Changed(vec![]),
Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))),
}
}
ty::PredicateAtom::ConstEquate(c1, c2) => {
debug!("equating consts: c1={:?} c2={:?}", c1, c2);
let stalled_on = &mut pending_obligation.stalled_on;
let mut evaluate = |c: &'tcx Const<'tcx>| {
if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val {
match self.selcx.infcx().const_eval_resolve(
obligation.param_env,
def,
substs,
promoted,
Some(obligation.cause.span),
) {
Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty)),
Err(ErrorHandled::TooGeneric) => {
stalled_on.append(
&mut substs
.types()
.filter_map(|ty| TyOrConstInferVar::maybe_from_ty(ty))
.collect(),
);
Err(ErrorHandled::TooGeneric)
}
Err(err) => Err(err),
}
} else {
Ok(c)
}
};
match (evaluate(c1), evaluate(c2)) {
(Ok(c1), Ok(c2)) => {
match self
.selcx
.infcx()
.at(&obligation.cause, obligation.param_env)
.eq(c1, c2)
{
Ok(_) => ProcessResult::Changed(vec![]),
Err(err) => ProcessResult::Error(
FulfillmentErrorCode::CodeConstEquateError(
ExpectedFound::new(true, c1, c2),
err,
),
),
}
}
(Err(ErrorHandled::Reported(ErrorReported)), _)
| (_, Err(ErrorHandled::Reported(ErrorReported))) => {
ProcessResult::Error(CodeSelectionError(ConstEvalFailure(
ErrorHandled::Reported(ErrorReported),
)))
}
(Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => {
span_bug!(
obligation.cause.span(self.selcx.tcx()),
"ConstEquate: const_eval_resolve returned an unexpected error"
)
}
(Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
ProcessResult::Unchanged
}
}
}
},
}
}
fn process_backedge<'c, I>(
&mut self,
cycle: I,
_marker: PhantomData<&'c PendingPredicateObligation<'tcx>>,
) where
I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>,
{
if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
debug!("process_child_obligations: coinductive match");
} else {
let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
self.selcx.infcx().report_overflow_error_cycle(&cycle);
}
}
}
impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
fn process_trait_obligation(
&mut self,
obligation: &PredicateObligation<'tcx>,
trait_obligation: TraitObligation<'tcx>,
stalled_on: &mut Vec<TyOrConstInferVar<'tcx>>,
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
let infcx = self.selcx.infcx();
if obligation.predicate.is_global() {
// no type variables present, can use evaluation for better caching.
// FIXME: consider caching errors too.
if infcx.predicate_must_hold_considering_regions(obligation) {
debug!(
"selecting trait `{:?}` at depth {} evaluated to holds",
obligation.predicate, obligation.recursion_depth
);
return ProcessResult::Changed(vec![]);
}
}
match self.selcx.select(&trait_obligation) {
Ok(Some(impl_source)) => {
debug!(
"selecting trait `{:?}` at depth {} yielded Ok(Some)",
trait_obligation.predicate, obligation.recursion_depth
);
ProcessResult::Changed(mk_pending(impl_source.nested_obligations()))
}
Ok(None) => {
debug!(
"selecting trait `{:?}` at depth {} yielded Ok(None)",
trait_obligation.predicate, obligation.recursion_depth
);
// This is a bit subtle: for the most part, the
// only reason we can fail to make progress on
// trait selection is because we don't have enough
// information about the types in the trait.
*stalled_on = trait_ref_infer_vars(
self.selcx,
trait_obligation.predicate.map_bound(|pred| pred.trait_ref),
);
debug!(
"process_predicate: pending obligation {:?} now stalled on {:?}",
infcx.resolve_vars_if_possible(obligation),
stalled_on
);
ProcessResult::Unchanged
}
Err(selection_err) => {
info!(
"selecting trait `{:?}` at depth {} yielded Err",
trait_obligation.predicate, obligation.recursion_depth
);
ProcessResult::Error(CodeSelectionError(selection_err))
}
}
}
fn process_projection_obligation(
&mut self,
project_obligation: PolyProjectionObligation<'tcx>,
stalled_on: &mut Vec<TyOrConstInferVar<'tcx>>,
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
let tcx = self.selcx.tcx();
match project::poly_project_and_unify_type(self.selcx, &project_obligation) {
Ok(Ok(Some(os))) => ProcessResult::Changed(mk_pending(os)),
Ok(Ok(None)) => {
*stalled_on = trait_ref_infer_vars(
self.selcx,
project_obligation.predicate.to_poly_trait_ref(self.selcx.tcx()),
);
ProcessResult::Unchanged
}
// Let the caller handle the recursion
Ok(Err(project::InProgress)) => ProcessResult::Changed(mk_pending(vec![
project_obligation.with(project_obligation.predicate.to_predicate(tcx)),
])),
Err(e) => ProcessResult::Error(CodeProjectionError(e)),
}
}
}
/// Returns the set of inference variables contained in a trait ref.
fn trait_ref_infer_vars<'a, 'tcx>(
selcx: &mut SelectionContext<'a, 'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> Vec<TyOrConstInferVar<'tcx>> {
selcx
.infcx()
.resolve_vars_if_possible(&trait_ref)
.skip_binder()
.substs
.iter()
// FIXME(eddyb) try using `skip_current_subtree` to skip everything that
// doesn't contain inference variables, not just the outermost level.
.filter(|arg| arg.has_infer_types_or_consts())
.flat_map(|arg| arg.walk())
.filter_map(TyOrConstInferVar::maybe_from_generic_arg)
.collect()
}
fn to_fulfillment_error<'tcx>(
error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>,
) -> FulfillmentError<'tcx> {
let obligation = error.backtrace.into_iter().next().unwrap().obligation;
FulfillmentError::new(obligation, error.error)
}

View file

@ -0,0 +1,74 @@
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
use crate::infer::InferCtxtExt as _;
use crate::traits::{self, ObligationCause};
use rustc_hir as hir;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
use crate::traits::error_reporting::InferCtxtExt;
#[derive(Clone)]
pub enum CopyImplementationError<'tcx> {
InfrigingFields(Vec<&'tcx ty::FieldDef>),
NotAnAdt,
HasDestructor,
}
pub fn can_type_implement_copy(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
self_type: Ty<'tcx>,
) -> Result<(), CopyImplementationError<'tcx>> {
// FIXME: (@jroesch) float this code up
tcx.infer_ctxt().enter(|infcx| {
let (adt, substs) = match self_type.kind {
// These types used to have a builtin impl.
// Now libcore provides that impl.
ty::Uint(_)
| ty::Int(_)
| ty::Bool
| ty::Float(_)
| ty::Char
| ty::RawPtr(..)
| ty::Never
| ty::Ref(_, _, hir::Mutability::Not) => return Ok(()),
ty::Adt(adt, substs) => (adt, substs),
_ => return Err(CopyImplementationError::NotAnAdt),
};
let mut infringing = Vec::new();
for variant in &adt.variants {
for field in &variant.fields {
let ty = field.ty(tcx, substs);
if ty.references_error() {
continue;
}
let span = tcx.def_span(field.did);
let cause = ObligationCause::dummy_with_span(span);
let ctx = traits::FulfillmentContext::new();
match traits::fully_normalize(&infcx, ctx, cause, param_env, &ty) {
Ok(ty) => {
if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
infringing.push(field);
}
}
Err(errors) => {
infcx.report_fulfillment_errors(&errors, None, false);
}
};
}
}
if !infringing.is_empty() {
return Err(CopyImplementationError::InfrigingFields(infringing));
}
if adt.has_dtor(tcx) {
return Err(CopyImplementationError::HasDestructor);
}
Ok(())
})
}

View file

@ -0,0 +1,563 @@
//! Trait Resolution. See the [rustc dev guide] for more information on how this works.
//!
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
#[allow(dead_code)]
pub mod auto_trait;
mod chalk_fulfill;
pub mod codegen;
mod coherence;
mod engine;
pub mod error_reporting;
mod fulfill;
pub mod misc;
mod object_safety;
mod on_unimplemented;
mod project;
pub mod query;
mod select;
mod specialize;
mod structural_match;
mod util;
pub mod wf;
use crate::infer::outlives::env::OutlivesEnvironment;
use crate::infer::{InferCtxt, RegionckMode, TyCtxtInferExt};
use crate::traits::error_reporting::InferCtxtExt as _;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
use rustc_middle::ty::{
self, GenericParamDefKind, ParamEnv, ToPredicate, Ty, TyCtxt, WithConstness,
};
use rustc_span::Span;
use std::fmt::Debug;
pub use self::FulfillmentErrorCode::*;
pub use self::ImplSource::*;
pub use self::ObligationCauseCode::*;
pub use self::SelectionError::*;
pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
pub use self::coherence::{OrphanCheckErr, OverlapResult};
pub use self::engine::TraitEngineExt;
pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
pub use self::object_safety::astconv_object_safety_violations;
pub use self::object_safety::is_vtable_safe_method;
pub use self::object_safety::MethodViolationCode;
pub use self::object_safety::ObjectSafetyViolation;
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
pub use self::project::{normalize, normalize_projection_type, normalize_to};
pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
pub use self::specialize::specialization_graph::FutureCompatOverlapError;
pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
pub use self::specialize::{specialization_graph, translate_substs, OverlapError};
pub use self::structural_match::search_for_structural_match_violation;
pub use self::structural_match::NonStructuralMatchTy;
pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs};
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
pub use self::util::{
get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices,
};
pub use self::util::{
supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits,
};
pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext;
pub use rustc_infer::traits::*;
/// Whether to skip the leak check, as part of a future compatibility warning step.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum SkipLeakCheck {
Yes,
No,
}
impl SkipLeakCheck {
fn is_yes(self) -> bool {
self == SkipLeakCheck::Yes
}
}
/// The "default" for skip-leak-check corresponds to the current
/// behavior (do not skip the leak check) -- not the behavior we are
/// transitioning into.
impl Default for SkipLeakCheck {
fn default() -> Self {
SkipLeakCheck::No
}
}
/// The mode that trait queries run in.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum TraitQueryMode {
// Standard/un-canonicalized queries get accurate
// spans etc. passed in and hence can do reasonable
// error reporting on their own.
Standard,
// Canonicalized queries get dummy spans and hence
// must generally propagate errors to
// pre-canonicalization callsites.
Canonical,
}
/// Creates predicate obligations from the generic bounds.
pub fn predicates_for_generics<'tcx>(
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
generic_bounds: ty::InstantiatedPredicates<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
util::predicates_for_generics(cause, 0, param_env, generic_bounds)
}
/// Determines whether the type `ty` is known to meet `bound` and
/// returns true if so. Returns false if `ty` either does not meet
/// `bound` or is not known to meet bound (note that this is
/// conservative towards *no impl*, which is the opposite of the
/// `evaluate` methods).
pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
def_id: DefId,
span: Span,
) -> bool {
debug!(
"type_known_to_meet_bound_modulo_regions(ty={:?}, bound={:?})",
ty,
infcx.tcx.def_path_str(def_id)
);
let trait_ref = ty::TraitRef { def_id, substs: infcx.tcx.mk_substs_trait(ty, &[]) };
let obligation = Obligation {
param_env,
cause: ObligationCause::misc(span, hir::CRATE_HIR_ID),
recursion_depth: 0,
predicate: trait_ref.without_const().to_predicate(infcx.tcx),
};
let result = infcx.predicate_must_hold_modulo_regions(&obligation);
debug!(
"type_known_to_meet_ty={:?} bound={} => {:?}",
ty,
infcx.tcx.def_path_str(def_id),
result
);
if result && ty.has_infer_types_or_consts() {
// Because of inference "guessing", selection can sometimes claim
// to succeed while the success requires a guess. To ensure
// this function's result remains infallible, we must confirm
// that guess. While imperfect, I believe this is sound.
// The handling of regions in this area of the code is terrible,
// see issue #29149. We should be able to improve on this with
// NLL.
let mut fulfill_cx = FulfillmentContext::new_ignoring_regions();
// We can use a dummy node-id here because we won't pay any mind
// to region obligations that arise (there shouldn't really be any
// anyhow).
let cause = ObligationCause::misc(span, hir::CRATE_HIR_ID);
fulfill_cx.register_bound(infcx, param_env, ty, def_id, cause);
// Note: we only assume something is `Copy` if we can
// *definitively* show that it implements `Copy`. Otherwise,
// assume it is move; linear is always ok.
match fulfill_cx.select_all_or_error(infcx) {
Ok(()) => {
debug!(
"type_known_to_meet_bound_modulo_regions: ty={:?} bound={} success",
ty,
infcx.tcx.def_path_str(def_id)
);
true
}
Err(e) => {
debug!(
"type_known_to_meet_bound_modulo_regions: ty={:?} bound={} errors={:?}",
ty,
infcx.tcx.def_path_str(def_id),
e
);
false
}
}
} else {
result
}
}
fn do_normalize_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
region_context: DefId,
cause: ObligationCause<'tcx>,
elaborated_env: ty::ParamEnv<'tcx>,
predicates: Vec<ty::Predicate<'tcx>>,
) -> Result<Vec<ty::Predicate<'tcx>>, ErrorReported> {
debug!(
"do_normalize_predicates(predicates={:?}, region_context={:?}, cause={:?})",
predicates, region_context, cause,
);
let span = cause.span;
tcx.infer_ctxt().enter(|infcx| {
// FIXME. We should really... do something with these region
// obligations. But this call just continues the older
// behavior (i.e., doesn't cause any new bugs), and it would
// take some further refactoring to actually solve them. In
// particular, we would have to handle implied bounds
// properly, and that code is currently largely confined to
// regionck (though I made some efforts to extract it
// out). -nmatsakis
//
// @arielby: In any case, these obligations are checked
// by wfcheck anyway, so I'm not sure we have to check
// them here too, and we will remove this function when
// we move over to lazy normalization *anyway*.
let fulfill_cx = FulfillmentContext::new_ignoring_regions();
let predicates =
match fully_normalize(&infcx, fulfill_cx, cause, elaborated_env, &predicates) {
Ok(predicates) => predicates,
Err(errors) => {
infcx.report_fulfillment_errors(&errors, None, false);
return Err(ErrorReported);
}
};
debug!("do_normalize_predictes: normalized predicates = {:?}", predicates);
// We can use the `elaborated_env` here; the region code only
// cares about declarations like `'a: 'b`.
let outlives_env = OutlivesEnvironment::new(elaborated_env);
infcx.resolve_regions_and_report_errors(
region_context,
&outlives_env,
RegionckMode::default(),
);
let predicates = match infcx.fully_resolve(&predicates) {
Ok(predicates) => predicates,
Err(fixup_err) => {
// If we encounter a fixup error, it means that some type
// variable wound up unconstrained. I actually don't know
// if this can happen, and I certainly don't expect it to
// happen often, but if it did happen it probably
// represents a legitimate failure due to some kind of
// unconstrained variable, and it seems better not to ICE,
// all things considered.
tcx.sess.span_err(span, &fixup_err.to_string());
return Err(ErrorReported);
}
};
if predicates.needs_infer() {
tcx.sess.delay_span_bug(span, "encountered inference variables after `fully_resolve`");
Err(ErrorReported)
} else {
Ok(predicates)
}
})
}
// FIXME: this is gonna need to be removed ...
/// Normalizes the parameter environment, reporting errors if they occur.
pub fn normalize_param_env_or_error<'tcx>(
tcx: TyCtxt<'tcx>,
region_context: DefId,
unnormalized_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
) -> ty::ParamEnv<'tcx> {
// I'm not wild about reporting errors here; I'd prefer to
// have the errors get reported at a defined place (e.g.,
// during typeck). Instead I have all parameter
// environments, in effect, going through this function
// and hence potentially reporting errors. This ensures of
// course that we never forget to normalize (the
// alternative seemed like it would involve a lot of
// manual invocations of this fn -- and then we'd have to
// deal with the errors at each of those sites).
//
// In any case, in practice, typeck constructs all the
// parameter environments once for every fn as it goes,
// and errors will get reported then; so after typeck we
// can be sure that no errors should occur.
debug!(
"normalize_param_env_or_error(region_context={:?}, unnormalized_env={:?}, cause={:?})",
region_context, unnormalized_env, cause
);
let mut predicates: Vec<_> =
util::elaborate_predicates(tcx, unnormalized_env.caller_bounds().into_iter())
.map(|obligation| obligation.predicate)
.collect();
debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
let elaborated_env = ty::ParamEnv::new(
tcx.intern_predicates(&predicates),
unnormalized_env.reveal(),
unnormalized_env.def_id,
);
// HACK: we are trying to normalize the param-env inside *itself*. The problem is that
// normalization expects its param-env to be already normalized, which means we have
// a circularity.
//
// The way we handle this is by normalizing the param-env inside an unnormalized version
// of the param-env, which means that if the param-env contains unnormalized projections,
// we'll have some normalization failures. This is unfortunate.
//
// Lazy normalization would basically handle this by treating just the
// normalizing-a-trait-ref-requires-itself cycles as evaluation failures.
//
// Inferred outlives bounds can create a lot of `TypeOutlives` predicates for associated
// types, so to make the situation less bad, we normalize all the predicates *but*
// the `TypeOutlives` predicates first inside the unnormalized parameter environment, and
// then we normalize the `TypeOutlives` bounds inside the normalized parameter environment.
//
// This works fairly well because trait matching does not actually care about param-env
// TypeOutlives predicates - these are normally used by regionck.
let outlives_predicates: Vec<_> = predicates
.drain_filter(|predicate| match predicate.skip_binders() {
ty::PredicateAtom::TypeOutlives(..) => true,
_ => false,
})
.collect();
debug!(
"normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})",
predicates, outlives_predicates
);
let non_outlives_predicates = match do_normalize_predicates(
tcx,
region_context,
cause.clone(),
elaborated_env,
predicates,
) {
Ok(predicates) => predicates,
// An unnormalized env is better than nothing.
Err(ErrorReported) => {
debug!("normalize_param_env_or_error: errored resolving non-outlives predicates");
return elaborated_env;
}
};
debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates);
// Not sure whether it is better to include the unnormalized TypeOutlives predicates
// here. I believe they should not matter, because we are ignoring TypeOutlives param-env
// predicates here anyway. Keeping them here anyway because it seems safer.
let outlives_env: Vec<_> =
non_outlives_predicates.iter().chain(&outlives_predicates).cloned().collect();
let outlives_env =
ty::ParamEnv::new(tcx.intern_predicates(&outlives_env), unnormalized_env.reveal(), None);
let outlives_predicates = match do_normalize_predicates(
tcx,
region_context,
cause,
outlives_env,
outlives_predicates,
) {
Ok(predicates) => predicates,
// An unnormalized env is better than nothing.
Err(ErrorReported) => {
debug!("normalize_param_env_or_error: errored resolving outlives predicates");
return elaborated_env;
}
};
debug!("normalize_param_env_or_error: outlives predicates={:?}", outlives_predicates);
let mut predicates = non_outlives_predicates;
predicates.extend(outlives_predicates);
debug!("normalize_param_env_or_error: final predicates={:?}", predicates);
ty::ParamEnv::new(
tcx.intern_predicates(&predicates),
unnormalized_env.reveal(),
unnormalized_env.def_id,
)
}
pub fn fully_normalize<'a, 'tcx, T>(
infcx: &InferCtxt<'a, 'tcx>,
mut fulfill_cx: FulfillmentContext<'tcx>,
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: &T,
) -> Result<T, Vec<FulfillmentError<'tcx>>>
where
T: TypeFoldable<'tcx>,
{
debug!("fully_normalize_with_fulfillcx(value={:?})", value);
let selcx = &mut SelectionContext::new(infcx);
let Normalized { value: normalized_value, obligations } =
project::normalize(selcx, param_env, cause, value);
debug!(
"fully_normalize: normalized_value={:?} obligations={:?}",
normalized_value, obligations
);
for obligation in obligations {
fulfill_cx.register_predicate_obligation(selcx.infcx(), obligation);
}
debug!("fully_normalize: select_all_or_error start");
fulfill_cx.select_all_or_error(infcx)?;
debug!("fully_normalize: select_all_or_error complete");
let resolved_value = infcx.resolve_vars_if_possible(&normalized_value);
debug!("fully_normalize: resolved_value={:?}", resolved_value);
Ok(resolved_value)
}
/// Normalizes the predicates and checks whether they hold in an empty environment. If this
/// returns true, then either normalize encountered an error or one of the predicates did not
/// hold. Used when creating vtables to check for unsatisfiable methods.
pub fn impossible_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
predicates: Vec<ty::Predicate<'tcx>>,
) -> bool {
debug!("impossible_predicates(predicates={:?})", predicates);
let result = tcx.infer_ctxt().enter(|infcx| {
let param_env = ty::ParamEnv::reveal_all();
let mut selcx = SelectionContext::new(&infcx);
let mut fulfill_cx = FulfillmentContext::new();
let cause = ObligationCause::dummy();
let Normalized { value: predicates, obligations } =
normalize(&mut selcx, param_env, cause.clone(), &predicates);
for obligation in obligations {
fulfill_cx.register_predicate_obligation(&infcx, obligation);
}
for predicate in predicates {
let obligation = Obligation::new(cause.clone(), param_env, predicate);
fulfill_cx.register_predicate_obligation(&infcx, obligation);
}
fulfill_cx.select_all_or_error(&infcx).is_err()
});
debug!("impossible_predicates(predicates={:?}) = {:?}", predicates, result);
result
}
fn subst_and_check_impossible_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
key: (DefId, SubstsRef<'tcx>),
) -> bool {
debug!("subst_and_check_impossible_predicates(key={:?})", key);
let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates;
predicates.retain(|predicate| !predicate.needs_subst());
let result = impossible_predicates(tcx, predicates);
debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result);
result
}
/// Given a trait `trait_ref`, iterates the vtable entries
/// that come from `trait_ref`, including its supertraits.
#[inline] // FIXME(#35870): avoid closures being unexported due to `impl Trait`.
fn vtable_methods<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] {
debug!("vtable_methods({:?})", trait_ref);
tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).flat_map(move |trait_ref| {
let trait_methods = tcx
.associated_items(trait_ref.def_id())
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Fn);
// Now list each method's DefId and InternalSubsts (for within its trait).
// If the method can never be called from this object, produce None.
trait_methods.map(move |trait_method| {
debug!("vtable_methods: trait_method={:?}", trait_method);
let def_id = trait_method.def_id;
// Some methods cannot be called on an object; skip those.
if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) {
debug!("vtable_methods: not vtable safe");
return None;
}
// The method may have some early-bound lifetimes; add regions for those.
let substs = trait_ref.map_bound(|trait_ref| {
InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind {
GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => {
trait_ref.substs[param.index as usize]
}
})
});
// The trait type may have higher-ranked lifetimes in it;
// erase them if they appear, so that we get the type
// at some particular call site.
let substs =
tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &substs);
// It's possible that the method relies on where-clauses that
// do not hold for this particular set of type parameters.
// Note that this method could then never be called, so we
// do not want to try and codegen it, in that case (see #23435).
let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
if impossible_predicates(tcx, predicates.predicates) {
debug!("vtable_methods: predicates do not hold");
return None;
}
Some((def_id, substs))
})
}))
}
/// Check whether a `ty` implements given trait(trait_def_id).
///
/// NOTE: Always return `false` for a type which needs inference.
fn type_implements_trait<'tcx>(
tcx: TyCtxt<'tcx>,
key: (
DefId, // trait_def_id,
Ty<'tcx>, // type
SubstsRef<'tcx>,
ParamEnv<'tcx>,
),
) -> bool {
let (trait_def_id, ty, params, param_env) = key;
debug!(
"type_implements_trait: trait_def_id={:?}, type={:?}, params={:?}, param_env={:?}",
trait_def_id, ty, params, param_env
);
let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, params) };
let obligation = Obligation {
cause: ObligationCause::dummy(),
param_env,
recursion_depth: 0,
predicate: trait_ref.without_const().to_predicate(tcx),
};
tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
}
pub fn provide(providers: &mut ty::query::Providers) {
object_safety::provide(providers);
structural_match::provide(providers);
*providers = ty::query::Providers {
specialization_graph_of: specialize::specialization_graph_provider,
specializes: specialize::specializes,
codegen_fulfill_obligation: codegen::codegen_fulfill_obligation,
vtable_methods,
type_implements_trait,
subst_and_check_impossible_predicates,
..*providers
};
}

View file

@ -0,0 +1,789 @@
//! "Object safety" refers to the ability for a trait to be converted
//! to an object. In general, traits may only be converted to an
//! object if all of their methods meet certain criteria. In particular,
//! they must:
//!
//! - have a suitable receiver from which we can extract a vtable and coerce to a "thin" version
//! that doesn't contain the vtable;
//! - not reference the erased type `Self` except for in this receiver;
//! - not have generic type parameters.
use super::elaborate_predicates;
use crate::infer::TyCtxtInferExt;
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::{self, Obligation, ObligationCause};
use rustc_errors::{Applicability, FatalError};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor, WithConstness};
use rustc_middle::ty::{Predicate, ToPredicate};
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
use rustc_span::symbol::Symbol;
use rustc_span::Span;
use smallvec::SmallVec;
use std::iter;
pub use crate::traits::{MethodViolationCode, ObjectSafetyViolation};
/// Returns the object safety violations that affect
/// astconv -- currently, `Self` in supertraits. This is needed
/// because `object_safety_violations` can't be used during
/// type collection.
pub fn astconv_object_safety_violations(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
) -> Vec<ObjectSafetyViolation> {
debug_assert!(tcx.generics_of(trait_def_id).has_self);
let violations = traits::supertrait_def_ids(tcx, trait_def_id)
.map(|def_id| predicates_reference_self(tcx, def_id, true))
.filter(|spans| !spans.is_empty())
.map(ObjectSafetyViolation::SupertraitSelf)
.collect();
debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}", trait_def_id, violations);
violations
}
fn object_safety_violations(
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
) -> &'tcx [ObjectSafetyViolation] {
debug_assert!(tcx.generics_of(trait_def_id).has_self);
debug!("object_safety_violations: {:?}", trait_def_id);
tcx.arena.alloc_from_iter(
traits::supertrait_def_ids(tcx, trait_def_id)
.flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id)),
)
}
/// We say a method is *vtable safe* if it can be invoked on a trait
/// object. Note that object-safe traits can have some
/// non-vtable-safe methods, so long as they require `Self: Sized` or
/// otherwise ensure that they cannot be used when `Self = Trait`.
pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: &ty::AssocItem) -> bool {
debug_assert!(tcx.generics_of(trait_def_id).has_self);
debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method);
// Any method that has a `Self: Sized` bound cannot be called.
if generics_require_sized_self(tcx, method.def_id) {
return false;
}
match virtual_call_violation_for_method(tcx, trait_def_id, method) {
None | Some(MethodViolationCode::WhereClauseReferencesSelf) => true,
Some(_) => false,
}
}
fn object_safety_violations_for_trait(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
) -> Vec<ObjectSafetyViolation> {
// Check methods for violations.
let mut violations: Vec<_> = tcx
.associated_items(trait_def_id)
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Fn)
.filter_map(|item| {
object_safety_violation_for_method(tcx, trait_def_id, &item)
.map(|(code, span)| ObjectSafetyViolation::Method(item.ident.name, code, span))
})
.filter(|violation| {
if let ObjectSafetyViolation::Method(
_,
MethodViolationCode::WhereClauseReferencesSelf,
span,
) = violation
{
// Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id.
// It's also hard to get a use site span, so we use the method definition span.
tcx.struct_span_lint_hir(
WHERE_CLAUSES_OBJECT_SAFETY,
hir::CRATE_HIR_ID,
*span,
|lint| {
let mut err = lint.build(&format!(
"the trait `{}` cannot be made into an object",
tcx.def_path_str(trait_def_id)
));
let node = tcx.hir().get_if_local(trait_def_id);
let msg = if let Some(hir::Node::Item(item)) = node {
err.span_label(
item.ident.span,
"this trait cannot be made into an object...",
);
format!("...because {}", violation.error_msg())
} else {
format!(
"the trait cannot be made into an object because {}",
violation.error_msg()
)
};
err.span_label(*span, &msg);
match (node, violation.solution()) {
(Some(_), Some((note, None))) => {
err.help(&note);
}
(Some(_), Some((note, Some((sugg, span))))) => {
err.span_suggestion(
span,
&note,
sugg,
Applicability::MachineApplicable,
);
}
// Only provide the help if its a local trait, otherwise it's not actionable.
_ => {}
}
err.emit();
},
);
false
} else {
true
}
})
.collect();
// Check the trait itself.
if trait_has_sized_self(tcx, trait_def_id) {
// We don't want to include the requirement from `Sized` itself to be `Sized` in the list.
let spans = get_sized_bounds(tcx, trait_def_id);
violations.push(ObjectSafetyViolation::SizedSelf(spans));
}
let spans = predicates_reference_self(tcx, trait_def_id, false);
if !spans.is_empty() {
violations.push(ObjectSafetyViolation::SupertraitSelf(spans));
}
violations.extend(
tcx.associated_items(trait_def_id)
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Const)
.map(|item| ObjectSafetyViolation::AssocConst(item.ident.name, item.ident.span)),
);
debug!(
"object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
trait_def_id, violations
);
violations
}
fn sized_trait_bound_spans<'tcx>(
tcx: TyCtxt<'tcx>,
bounds: hir::GenericBounds<'tcx>,
) -> impl 'tcx + Iterator<Item = Span> {
bounds.iter().filter_map(move |b| match b {
hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None)
if trait_has_sized_self(
tcx,
trait_ref.trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()),
) =>
{
// Fetch spans for supertraits that are `Sized`: `trait T: Super`
Some(trait_ref.span)
}
_ => None,
})
}
fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> {
tcx.hir()
.get_if_local(trait_def_id)
.and_then(|node| match node {
hir::Node::Item(hir::Item {
kind: hir::ItemKind::Trait(.., generics, bounds, _),
..
}) => Some(
generics
.where_clause
.predicates
.iter()
.filter_map(|pred| {
match pred {
hir::WherePredicate::BoundPredicate(pred)
if pred.bounded_ty.hir_id.owner.to_def_id() == trait_def_id =>
{
// Fetch spans for trait bounds that are Sized:
// `trait T where Self: Pred`
Some(sized_trait_bound_spans(tcx, pred.bounds))
}
_ => None,
}
})
.flatten()
// Fetch spans for supertraits that are `Sized`: `trait T: Super`.
.chain(sized_trait_bound_spans(tcx, bounds))
.collect::<SmallVec<[Span; 1]>>(),
),
_ => None,
})
.unwrap_or_else(SmallVec::new)
}
fn predicates_reference_self(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
supertraits_only: bool,
) -> SmallVec<[Span; 1]> {
let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id));
let predicates = if supertraits_only {
tcx.super_predicates_of(trait_def_id)
} else {
tcx.predicates_of(trait_def_id)
};
let self_ty = tcx.types.self_param;
let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into());
predicates
.predicates
.iter()
.map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp))
.filter_map(|(predicate, &sp)| {
match predicate.skip_binders() {
ty::PredicateAtom::Trait(ref data, _) => {
// In the case of a trait predicate, we can skip the "self" type.
if data.trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None }
}
ty::PredicateAtom::Projection(ref data) => {
// And similarly for projections. This should be redundant with
// the previous check because any projection should have a
// matching `Trait` predicate with the same inputs, but we do
// the check to be safe.
//
// Note that we *do* allow projection *outputs* to contain
// `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`),
// we just require the user to specify *both* outputs
// in the object type (i.e., `dyn Foo<Output=(), Result=()>`).
//
// This is ALT2 in issue #56288, see that for discussion of the
// possible alternatives.
if data.projection_ty.trait_ref(tcx).substs[1..].iter().any(has_self_ty) {
Some(sp)
} else {
None
}
}
ty::PredicateAtom::WellFormed(..)
| ty::PredicateAtom::ObjectSafe(..)
| ty::PredicateAtom::TypeOutlives(..)
| ty::PredicateAtom::RegionOutlives(..)
| ty::PredicateAtom::ClosureKind(..)
| ty::PredicateAtom::Subtype(..)
| ty::PredicateAtom::ConstEvaluatable(..)
| ty::PredicateAtom::ConstEquate(..) => None,
}
})
.collect()
}
fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
generics_require_sized_self(tcx, trait_def_id)
}
fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let sized_def_id = match tcx.lang_items().sized_trait() {
Some(def_id) => def_id,
None => {
return false; /* No Sized trait, can't require it! */
}
};
// Search for a predicate like `Self : Sized` amongst the trait bounds.
let predicates = tcx.predicates_of(def_id);
let predicates = predicates.instantiate_identity(tcx).predicates;
elaborate_predicates(tcx, predicates.into_iter()).any(|obligation| {
match obligation.predicate.skip_binders() {
ty::PredicateAtom::Trait(ref trait_pred, _) => {
trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0)
}
ty::PredicateAtom::Projection(..)
| ty::PredicateAtom::Subtype(..)
| ty::PredicateAtom::RegionOutlives(..)
| ty::PredicateAtom::WellFormed(..)
| ty::PredicateAtom::ObjectSafe(..)
| ty::PredicateAtom::ClosureKind(..)
| ty::PredicateAtom::TypeOutlives(..)
| ty::PredicateAtom::ConstEvaluatable(..)
| ty::PredicateAtom::ConstEquate(..) => false,
}
})
}
/// Returns `Some(_)` if this method makes the containing trait not object safe.
fn object_safety_violation_for_method(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
method: &ty::AssocItem,
) -> Option<(MethodViolationCode, Span)> {
debug!("object_safety_violation_for_method({:?}, {:?})", trait_def_id, method);
// Any method that has a `Self : Sized` requisite is otherwise
// exempt from the regulations.
if generics_require_sized_self(tcx, method.def_id) {
return None;
}
let violation = virtual_call_violation_for_method(tcx, trait_def_id, method);
// Get an accurate span depending on the violation.
violation.map(|v| {
let node = tcx.hir().get_if_local(method.def_id);
let span = match (v, node) {
(MethodViolationCode::ReferencesSelfInput(arg), Some(node)) => node
.fn_decl()
.and_then(|decl| decl.inputs.get(arg + 1))
.map_or(method.ident.span, |arg| arg.span),
(MethodViolationCode::UndispatchableReceiver, Some(node)) => node
.fn_decl()
.and_then(|decl| decl.inputs.get(0))
.map_or(method.ident.span, |arg| arg.span),
(MethodViolationCode::ReferencesSelfOutput, Some(node)) => {
node.fn_decl().map_or(method.ident.span, |decl| decl.output.span())
}
_ => method.ident.span,
};
(v, span)
})
}
/// Returns `Some(_)` if this method cannot be called on a trait
/// object; this does not necessarily imply that the enclosing trait
/// is not object safe, because the method might have a where clause
/// `Self:Sized`.
fn virtual_call_violation_for_method<'tcx>(
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
method: &ty::AssocItem,
) -> Option<MethodViolationCode> {
// The method's first parameter must be named `self`
if !method.fn_has_self_parameter {
// We'll attempt to provide a structured suggestion for `Self: Sized`.
let sugg =
tcx.hir().get_if_local(method.def_id).as_ref().and_then(|node| node.generics()).map(
|generics| match generics.where_clause.predicates {
[] => (" where Self: Sized", generics.where_clause.span),
[.., pred] => (", Self: Sized", pred.span().shrink_to_hi()),
},
);
return Some(MethodViolationCode::StaticMethod(sugg));
}
let sig = tcx.fn_sig(method.def_id);
for (i, input_ty) in sig.skip_binder().inputs()[1..].iter().enumerate() {
if contains_illegal_self_type_reference(tcx, trait_def_id, input_ty) {
return Some(MethodViolationCode::ReferencesSelfInput(i));
}
}
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output().skip_binder()) {
return Some(MethodViolationCode::ReferencesSelfOutput);
}
// We can't monomorphize things like `fn foo<A>(...)`.
let own_counts = tcx.generics_of(method.def_id).own_counts();
if own_counts.types + own_counts.consts != 0 {
return Some(MethodViolationCode::Generic);
}
if tcx
.predicates_of(method.def_id)
.predicates
.iter()
// A trait object can't claim to live more than the concrete type,
// so outlives predicates will always hold.
.cloned()
.filter(|(p, _)| p.to_opt_type_outlives().is_none())
.collect::<Vec<_>>()
// Do a shallow visit so that `contains_illegal_self_type_reference`
// may apply it's custom visiting.
.visit_tys_shallow(|t| contains_illegal_self_type_reference(tcx, trait_def_id, t))
{
return Some(MethodViolationCode::WhereClauseReferencesSelf);
}
let receiver_ty =
tcx.liberate_late_bound_regions(method.def_id, &sig.map_bound(|sig| sig.inputs()[0]));
// Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on.
// However, this is already considered object-safe. We allow it as a special case here.
// FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows
// `Receiver: Unsize<Receiver[Self => dyn Trait]>`.
if receiver_ty != tcx.types.self_param {
if !receiver_is_dispatchable(tcx, method, receiver_ty) {
return Some(MethodViolationCode::UndispatchableReceiver);
} else {
// Do sanity check to make sure the receiver actually has the layout of a pointer.
use rustc_target::abi::Abi;
let param_env = tcx.param_env(method.def_id);
let abi_of_ty = |ty: Ty<'tcx>| -> &Abi {
match tcx.layout_of(param_env.and(ty)) {
Ok(layout) => &layout.abi,
Err(err) => bug!("error: {}\n while computing layout for type {:?}", err, ty),
}
};
// e.g., `Rc<()>`
let unit_receiver_ty =
receiver_for_self_ty(tcx, receiver_ty, tcx.mk_unit(), method.def_id);
match abi_of_ty(unit_receiver_ty) {
&Abi::Scalar(..) => (),
abi => {
tcx.sess.delay_span_bug(
tcx.def_span(method.def_id),
&format!(
"receiver when `Self = ()` should have a Scalar ABI; found {:?}",
abi
),
);
}
}
let trait_object_ty =
object_ty_for_trait(tcx, trait_def_id, tcx.mk_region(ty::ReStatic));
// e.g., `Rc<dyn Trait>`
let trait_object_receiver =
receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method.def_id);
match abi_of_ty(trait_object_receiver) {
&Abi::ScalarPair(..) => (),
abi => {
tcx.sess.delay_span_bug(
tcx.def_span(method.def_id),
&format!(
"receiver when `Self = {}` should have a ScalarPair ABI; \
found {:?}",
trait_object_ty, abi
),
);
}
}
}
}
None
}
/// Performs a type substitution to produce the version of `receiver_ty` when `Self = self_ty`.
/// For example, for `receiver_ty = Rc<Self>` and `self_ty = Foo`, returns `Rc<Foo>`.
fn receiver_for_self_ty<'tcx>(
tcx: TyCtxt<'tcx>,
receiver_ty: Ty<'tcx>,
self_ty: Ty<'tcx>,
method_def_id: DefId,
) -> Ty<'tcx> {
debug!("receiver_for_self_ty({:?}, {:?}, {:?})", receiver_ty, self_ty, method_def_id);
let substs = InternalSubsts::for_item(tcx, method_def_id, |param, _| {
if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) }
});
let result = receiver_ty.subst(tcx, substs);
debug!(
"receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}",
receiver_ty, self_ty, method_def_id, result
);
result
}
/// Creates the object type for the current trait. For example,
/// if the current trait is `Deref`, then this will be
/// `dyn Deref<Target = Self::Target> + 'static`.
fn object_ty_for_trait<'tcx>(
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
lifetime: ty::Region<'tcx>,
) -> Ty<'tcx> {
debug!("object_ty_for_trait: trait_def_id={:?}", trait_def_id);
let trait_ref = ty::TraitRef::identity(tcx, trait_def_id);
let trait_predicate =
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
let mut associated_types = traits::supertraits(tcx, ty::Binder::dummy(trait_ref))
.flat_map(|super_trait_ref| {
tcx.associated_items(super_trait_ref.def_id())
.in_definition_order()
.map(move |item| (super_trait_ref, item))
})
.filter(|(_, item)| item.kind == ty::AssocKind::Type)
.collect::<Vec<_>>();
// existential predicates need to be in a specific order
associated_types.sort_by_cached_key(|(_, item)| tcx.def_path_hash(item.def_id));
let projection_predicates = associated_types.into_iter().map(|(super_trait_ref, item)| {
// We *can* get bound lifetimes here in cases like
// `trait MyTrait: for<'s> OtherTrait<&'s T, Output=bool>`.
//
// binder moved to (*)...
let super_trait_ref = super_trait_ref.skip_binder();
ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
ty: tcx.mk_projection(item.def_id, super_trait_ref.substs),
item_def_id: item.def_id,
substs: super_trait_ref.substs,
})
});
let existential_predicates =
tcx.mk_existential_predicates(iter::once(trait_predicate).chain(projection_predicates));
let object_ty = tcx.mk_dynamic(
// (*) ... binder re-introduced here
ty::Binder::bind(existential_predicates),
lifetime,
);
debug!("object_ty_for_trait: object_ty=`{}`", object_ty);
object_ty
}
/// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a
/// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type
/// in the following way:
/// - let `Receiver` be the type of the `self` argument, i.e `Self`, `&Self`, `Rc<Self>`,
/// - require the following bound:
///
/// ```
/// Receiver[Self => T]: DispatchFromDyn<Receiver[Self => dyn Trait]>
/// ```
///
/// where `Foo[X => Y]` means "the same type as `Foo`, but with `X` replaced with `Y`"
/// (substitution notation).
///
/// Some examples of receiver types and their required obligation:
/// - `&'a mut self` requires `&'a mut Self: DispatchFromDyn<&'a mut dyn Trait>`,
/// - `self: Rc<Self>` requires `Rc<Self>: DispatchFromDyn<Rc<dyn Trait>>`,
/// - `self: Pin<Box<Self>>` requires `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<dyn Trait>>>`.
///
/// The only case where the receiver is not dispatchable, but is still a valid receiver
/// type (just not object-safe), is when there is more than one level of pointer indirection.
/// E.g., `self: &&Self`, `self: &Rc<Self>`, `self: Box<Box<Self>>`. In these cases, there
/// is no way, or at least no inexpensive way, to coerce the receiver from the version where
/// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type
/// contained by the trait object, because the object that needs to be coerced is behind
/// a pointer.
///
/// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result
/// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch
/// is stabilized, see tracking issue https://github.com/rust-lang/rust/issues/43561).
/// Instead, we fudge a little by introducing a new type parameter `U` such that
/// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`.
/// Written as a chalk-style query:
///
/// forall (U: Trait + ?Sized) {
/// if (Self: Unsize<U>) {
/// Receiver: DispatchFromDyn<Receiver[Self => U]>
/// }
/// }
///
/// for `self: &'a mut Self`, this means `&'a mut Self: DispatchFromDyn<&'a mut U>`
/// for `self: Rc<Self>`, this means `Rc<Self>: DispatchFromDyn<Rc<U>>`
/// for `self: Pin<Box<Self>>`, this means `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<U>>>`
//
// FIXME(mikeyhew) when unsized receivers are implemented as part of unsized rvalues, add this
// fallback query: `Receiver: Unsize<Receiver[Self => U]>` to support receivers like
// `self: Wrapper<Self>`.
#[allow(dead_code)]
fn receiver_is_dispatchable<'tcx>(
tcx: TyCtxt<'tcx>,
method: &ty::AssocItem,
receiver_ty: Ty<'tcx>,
) -> bool {
debug!("receiver_is_dispatchable: method = {:?}, receiver_ty = {:?}", method, receiver_ty);
let traits = (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait());
let (unsize_did, dispatch_from_dyn_did) = if let (Some(u), Some(cu)) = traits {
(u, cu)
} else {
debug!("receiver_is_dispatchable: Missing Unsize or DispatchFromDyn traits");
return false;
};
// the type `U` in the query
// use a bogus type parameter to mimic a forall(U) query using u32::MAX for now.
// FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can
// replace this with `dyn Trait`
let unsized_self_ty: Ty<'tcx> =
tcx.mk_ty_param(u32::MAX, Symbol::intern("RustaceansAreAwesome"));
// `Receiver[Self => U]`
let unsized_receiver_ty =
receiver_for_self_ty(tcx, receiver_ty, unsized_self_ty, method.def_id);
// create a modified param env, with `Self: Unsize<U>` and `U: Trait` added to caller bounds
// `U: ?Sized` is already implied here
let param_env = {
let param_env = tcx.param_env(method.def_id);
// Self: Unsize<U>
let unsize_predicate = ty::TraitRef {
def_id: unsize_did,
substs: tcx.mk_substs_trait(tcx.types.self_param, &[unsized_self_ty.into()]),
}
.without_const()
.to_predicate(tcx);
// U: Trait<Arg1, ..., ArgN>
let trait_predicate = {
let substs =
InternalSubsts::for_item(tcx, method.container.assert_trait(), |param, _| {
if param.index == 0 {
unsized_self_ty.into()
} else {
tcx.mk_param_from_def(param)
}
});
ty::TraitRef { def_id: unsize_did, substs }.without_const().to_predicate(tcx)
};
let caller_bounds: Vec<Predicate<'tcx>> = param_env
.caller_bounds()
.iter()
.chain(iter::once(unsize_predicate))
.chain(iter::once(trait_predicate))
.collect();
ty::ParamEnv::new(
tcx.intern_predicates(&caller_bounds),
param_env.reveal(),
param_env.def_id,
)
};
// Receiver: DispatchFromDyn<Receiver[Self => U]>
let obligation = {
let predicate = ty::TraitRef {
def_id: dispatch_from_dyn_did,
substs: tcx.mk_substs_trait(receiver_ty, &[unsized_receiver_ty.into()]),
}
.without_const()
.to_predicate(tcx);
Obligation::new(ObligationCause::dummy(), param_env, predicate)
};
tcx.infer_ctxt().enter(|ref infcx| {
// the receiver is dispatchable iff the obligation holds
infcx.predicate_must_hold_modulo_regions(&obligation)
})
}
fn contains_illegal_self_type_reference<'tcx>(
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
ty: Ty<'tcx>,
) -> bool {
// This is somewhat subtle. In general, we want to forbid
// references to `Self` in the argument and return types,
// since the value of `Self` is erased. However, there is one
// exception: it is ok to reference `Self` in order to access
// an associated type of the current trait, since we retain
// the value of those associated types in the object type
// itself.
//
// ```rust
// trait SuperTrait {
// type X;
// }
//
// trait Trait : SuperTrait {
// type Y;
// fn foo(&self, x: Self) // bad
// fn foo(&self) -> Self // bad
// fn foo(&self) -> Option<Self> // bad
// fn foo(&self) -> Self::Y // OK, desugars to next example
// fn foo(&self) -> <Self as Trait>::Y // OK
// fn foo(&self) -> Self::X // OK, desugars to next example
// fn foo(&self) -> <Self as SuperTrait>::X // OK
// }
// ```
//
// However, it is not as simple as allowing `Self` in a projected
// type, because there are illegal ways to use `Self` as well:
//
// ```rust
// trait Trait : SuperTrait {
// ...
// fn foo(&self) -> <Self as SomeOtherTrait>::X;
// }
// ```
//
// Here we will not have the type of `X` recorded in the
// object type, and we cannot resolve `Self as SomeOtherTrait`
// without knowing what `Self` is.
struct IllegalSelfTypeVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
self_ty: Ty<'tcx>,
trait_def_id: DefId,
supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>>,
}
impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> {
fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
match t.kind {
ty::Param(_) => t == self.self_ty,
ty::Projection(ref data) => {
// This is a projected type `<Foo as SomeTrait>::X`.
// Compute supertraits of current trait lazily.
if self.supertraits.is_none() {
let trait_ref =
ty::Binder::bind(ty::TraitRef::identity(self.tcx, self.trait_def_id));
self.supertraits = Some(traits::supertraits(self.tcx, trait_ref).collect());
}
// Determine whether the trait reference `Foo as
// SomeTrait` is in fact a supertrait of the
// current trait. In that case, this type is
// legal, because the type `X` will be specified
// in the object type. Note that we can just use
// direct equality here because all of these types
// are part of the formal parameter listing, and
// hence there should be no inference variables.
let projection_trait_ref = ty::Binder::bind(data.trait_ref(self.tcx));
let is_supertrait_of_current_trait =
self.supertraits.as_ref().unwrap().contains(&projection_trait_ref);
if is_supertrait_of_current_trait {
false // do not walk contained types, do not report error, do collect $200
} else {
t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error
}
}
_ => t.super_visit_with(self), // walk contained types, if any
}
}
fn visit_const(&mut self, _c: &ty::Const<'tcx>) -> bool {
// FIXME(#72219) Look into the unevaluated constants for object safety violations.
// Do not walk substitutions of unevaluated consts, as they contain `Self`, even
// though the const expression doesn't necessary use it. Currently type variables
// inside array length expressions are forbidden, so they can't break the above
// rules.
false
}
}
ty.visit_with(&mut IllegalSelfTypeVisitor {
tcx,
self_ty: tcx.types.self_param,
trait_def_id,
supertraits: None,
})
}
pub fn provide(providers: &mut ty::query::Providers) {
*providers = ty::query::Providers { object_safety_violations, ..*providers };
}

View file

@ -0,0 +1,385 @@
use rustc_ast::{MetaItem, NestedMetaItem};
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{struct_span_err, ErrorReported};
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
#[derive(Clone, Debug)]
pub struct OnUnimplementedFormatString(Symbol);
#[derive(Debug)]
pub struct OnUnimplementedDirective {
pub condition: Option<MetaItem>,
pub subcommands: Vec<OnUnimplementedDirective>,
pub message: Option<OnUnimplementedFormatString>,
pub label: Option<OnUnimplementedFormatString>,
pub note: Option<OnUnimplementedFormatString>,
pub enclosing_scope: Option<OnUnimplementedFormatString>,
}
#[derive(Default)]
pub struct OnUnimplementedNote {
pub message: Option<String>,
pub label: Option<String>,
pub note: Option<String>,
pub enclosing_scope: Option<String>,
}
fn parse_error(
tcx: TyCtxt<'_>,
span: Span,
message: &str,
label: &str,
note: Option<&str>,
) -> ErrorReported {
let mut diag = struct_span_err!(tcx.sess, span, E0232, "{}", message);
diag.span_label(span, label);
if let Some(note) = note {
diag.note(note);
}
diag.emit();
ErrorReported
}
impl<'tcx> OnUnimplementedDirective {
fn parse(
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
items: &[NestedMetaItem],
span: Span,
is_root: bool,
) -> Result<Self, ErrorReported> {
let mut errored = false;
let mut item_iter = items.iter();
let condition = if is_root {
None
} else {
let cond = item_iter
.next()
.ok_or_else(|| {
parse_error(
tcx,
span,
"empty `on`-clause in `#[rustc_on_unimplemented]`",
"empty on-clause here",
None,
)
})?
.meta_item()
.ok_or_else(|| {
parse_error(
tcx,
span,
"invalid `on`-clause in `#[rustc_on_unimplemented]`",
"invalid on-clause here",
None,
)
})?;
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |_| true);
Some(cond.clone())
};
let mut message = None;
let mut label = None;
let mut note = None;
let mut enclosing_scope = None;
let mut subcommands = vec![];
let parse_value = |value_str| {
OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span).map(Some)
};
for item in item_iter {
if item.has_name(sym::message) && message.is_none() {
if let Some(message_) = item.value_str() {
message = parse_value(message_)?;
continue;
}
} else if item.has_name(sym::label) && label.is_none() {
if let Some(label_) = item.value_str() {
label = parse_value(label_)?;
continue;
}
} else if item.has_name(sym::note) && note.is_none() {
if let Some(note_) = item.value_str() {
note = parse_value(note_)?;
continue;
}
} else if item.has_name(sym::enclosing_scope) && enclosing_scope.is_none() {
if let Some(enclosing_scope_) = item.value_str() {
enclosing_scope = parse_value(enclosing_scope_)?;
continue;
}
} else if item.has_name(sym::on)
&& is_root
&& message.is_none()
&& label.is_none()
&& note.is_none()
{
if let Some(items) = item.meta_item_list() {
if let Ok(subcommand) =
Self::parse(tcx, trait_def_id, &items, item.span(), false)
{
subcommands.push(subcommand);
} else {
errored = true;
}
continue;
}
}
// nothing found
parse_error(
tcx,
item.span(),
"this attribute must have a valid value",
"expected value here",
Some(r#"eg `#[rustc_on_unimplemented(message="foo")]`"#),
);
}
if errored {
Err(ErrorReported)
} else {
Ok(OnUnimplementedDirective {
condition,
subcommands,
message,
label,
note,
enclosing_scope,
})
}
}
pub fn of_item(
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
impl_def_id: DefId,
) -> Result<Option<Self>, ErrorReported> {
let attrs = tcx.get_attrs(impl_def_id);
let attr = if let Some(item) = tcx.sess.find_by_name(&attrs, sym::rustc_on_unimplemented) {
item
} else {
return Ok(None);
};
let result = if let Some(items) = attr.meta_item_list() {
Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some)
} else if let Some(value) = attr.value_str() {
Ok(Some(OnUnimplementedDirective {
condition: None,
message: None,
subcommands: vec![],
label: Some(OnUnimplementedFormatString::try_parse(
tcx,
trait_def_id,
value,
attr.span,
)?),
note: None,
enclosing_scope: None,
}))
} else {
return Err(ErrorReported);
};
debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result);
result
}
pub fn evaluate(
&self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
options: &[(Symbol, Option<String>)],
) -> OnUnimplementedNote {
let mut message = None;
let mut label = None;
let mut note = None;
let mut enclosing_scope = None;
info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
for command in self.subcommands.iter().chain(Some(self)).rev() {
if let Some(ref condition) = command.condition {
if !attr::eval_condition(
condition,
&tcx.sess.parse_sess,
Some(tcx.features()),
&mut |c| {
c.ident().map_or(false, |ident| {
options.contains(&(ident.name, c.value_str().map(|s| s.to_string())))
})
},
) {
debug!("evaluate: skipping {:?} due to condition", command);
continue;
}
}
debug!("evaluate: {:?} succeeded", command);
if let Some(ref message_) = command.message {
message = Some(message_.clone());
}
if let Some(ref label_) = command.label {
label = Some(label_.clone());
}
if let Some(ref note_) = command.note {
note = Some(note_.clone());
}
if let Some(ref enclosing_scope_) = command.enclosing_scope {
enclosing_scope = Some(enclosing_scope_.clone());
}
}
let options: FxHashMap<Symbol, String> =
options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect();
OnUnimplementedNote {
label: label.map(|l| l.format(tcx, trait_ref, &options)),
message: message.map(|m| m.format(tcx, trait_ref, &options)),
note: note.map(|n| n.format(tcx, trait_ref, &options)),
enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options)),
}
}
}
impl<'tcx> OnUnimplementedFormatString {
fn try_parse(
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
from: Symbol,
err_sp: Span,
) -> Result<Self, ErrorReported> {
let result = OnUnimplementedFormatString(from);
result.verify(tcx, trait_def_id, err_sp)?;
Ok(result)
}
fn verify(
&self,
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
span: Span,
) -> Result<(), ErrorReported> {
let name = tcx.item_name(trait_def_id);
let generics = tcx.generics_of(trait_def_id);
let s = self.0.as_str();
let parser = Parser::new(&s, None, None, false, ParseMode::Format);
let mut result = Ok(());
for token in parser {
match token {
Piece::String(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => match a.position {
// `{Self}` is allowed
Position::ArgumentNamed(s) if s == kw::SelfUpper => (),
// `{ThisTraitsName}` is allowed
Position::ArgumentNamed(s) if s == name => (),
// `{from_method}` is allowed
Position::ArgumentNamed(s) if s == sym::from_method => (),
// `{from_desugaring}` is allowed
Position::ArgumentNamed(s) if s == sym::from_desugaring => (),
// `{ItemContext}` is allowed
Position::ArgumentNamed(s) if s == sym::ItemContext => (),
// So is `{A}` if A is a type parameter
Position::ArgumentNamed(s) => {
match generics.params.iter().find(|param| param.name == s) {
Some(_) => (),
None => {
struct_span_err!(
tcx.sess,
span,
E0230,
"there is no parameter `{}` on trait `{}`",
s,
name
)
.emit();
result = Err(ErrorReported);
}
}
}
// `{:1}` and `{}` are not to be used
Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => {
struct_span_err!(
tcx.sess,
span,
E0231,
"only named substitution parameters are allowed"
)
.emit();
result = Err(ErrorReported);
}
},
}
}
result
}
pub fn format(
&self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
options: &FxHashMap<Symbol, String>,
) -> String {
let name = tcx.item_name(trait_ref.def_id);
let trait_str = tcx.def_path_str(trait_ref.def_id);
let generics = tcx.generics_of(trait_ref.def_id);
let generic_map = generics
.params
.iter()
.filter_map(|param| {
let value = match param.kind {
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => {
trait_ref.substs[param.index as usize].to_string()
}
GenericParamDefKind::Lifetime => return None,
};
let name = param.name;
Some((name, value))
})
.collect::<FxHashMap<Symbol, String>>();
let empty_string = String::new();
let s = self.0.as_str();
let parser = Parser::new(&s, None, None, false, ParseMode::Format);
let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
parser
.map(|p| match p {
Piece::String(s) => s,
Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(s) => match generic_map.get(&s) {
Some(val) => val,
None if s == name => &trait_str,
None => {
if let Some(val) = options.get(&s) {
val
} else if s == sym::from_desugaring || s == sym::from_method {
// don't break messages using these two arguments incorrectly
&empty_string
} else if s == sym::ItemContext {
&item_context
} else {
bug!(
"broken on_unimplemented {:?} for {:?}: \
no argument matching {:?}",
self.0,
trait_ref,
s
)
}
}
},
_ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
},
})
.collect()
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,139 @@
use crate::infer::at::At;
use crate::infer::canonical::OriginalQueryValues;
use crate::infer::InferOk;
use rustc_middle::ty::subst::GenericArg;
use rustc_middle::ty::{self, Ty, TyCtxt};
pub use rustc_middle::traits::query::{DropckOutlivesResult, DtorckConstraint};
pub trait AtExt<'tcx> {
fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec<GenericArg<'tcx>>>;
}
impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
/// Given a type `ty` of some value being dropped, computes a set
/// of "kinds" (types, regions) that must be outlive the execution
/// of the destructor. These basically correspond to data that the
/// destructor might access. This is used during regionck to
/// impose "outlives" constraints on any lifetimes referenced
/// within.
///
/// The rules here are given by the "dropck" RFCs, notably [#1238]
/// and [#1327]. This is a fixed-point computation, where we
/// explore all the data that will be dropped (transitively) when
/// a value of type `ty` is dropped. For each type T that will be
/// dropped and which has a destructor, we must assume that all
/// the types/regions of T are live during the destructor, unless
/// they are marked with a special attribute (`#[may_dangle]`).
///
/// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md
/// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md
fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec<GenericArg<'tcx>>> {
debug!("dropck_outlives(ty={:?}, param_env={:?})", ty, self.param_env,);
// Quick check: there are a number of cases that we know do not require
// any destructor.
let tcx = self.infcx.tcx;
if trivial_dropck_outlives(tcx, ty) {
return InferOk { value: vec![], obligations: vec![] };
}
let mut orig_values = OriginalQueryValues::default();
let c_ty = self.infcx.canonicalize_query(&self.param_env.and(ty), &mut orig_values);
let span = self.cause.span;
debug!("c_ty = {:?}", c_ty);
if let Ok(result) = &tcx.dropck_outlives(c_ty) {
if result.is_proven() {
if let Ok(InferOk { value, obligations }) =
self.infcx.instantiate_query_response_and_region_obligations(
self.cause,
self.param_env,
&orig_values,
result,
)
{
let ty = self.infcx.resolve_vars_if_possible(&ty);
let kinds = value.into_kinds_reporting_overflows(tcx, span, ty);
return InferOk { value: kinds, obligations };
}
}
}
// Errors and ambiuity in dropck occur in two cases:
// - unresolved inference variables at the end of typeck
// - non well-formed types where projections cannot be resolved
// Either of these should have created an error before.
tcx.sess.delay_span_bug(span, "dtorck encountered internal error");
InferOk { value: vec![], obligations: vec![] }
}
}
/// This returns true if the type `ty` is "trivial" for
/// dropck-outlives -- that is, if it doesn't require any types to
/// outlive. This is similar but not *quite* the same as the
/// `needs_drop` test in the compiler already -- that is, for every
/// type T for which this function return true, needs-drop would
/// return `false`. But the reverse does not hold: in particular,
/// `needs_drop` returns false for `PhantomData`, but it is not
/// trivial for dropck-outlives.
///
/// Note also that `needs_drop` requires a "global" type (i.e., one
/// with erased regions), but this function does not.
pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
match ty.kind {
// None of these types have a destructor and hence they do not
// require anything in particular to outlive the dtor's
// execution.
ty::Infer(ty::FreshIntTy(_))
| ty::Infer(ty::FreshFloatTy(_))
| ty::Bool
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Never
| ty::FnDef(..)
| ty::FnPtr(_)
| ty::Char
| ty::GeneratorWitness(..)
| ty::RawPtr(_)
| ty::Ref(..)
| ty::Str
| ty::Foreign(..)
| ty::Error(_) => true,
// [T; N] and [T] have same properties as T.
ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty),
// (T1..Tn) and closures have same properties as T1..Tn --
// check if *any* of those are trivial.
ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())),
ty::Closure(_, ref substs) => {
substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t))
}
ty::Adt(def, _) => {
if Some(def.did) == tcx.lang_items().manually_drop() {
// `ManuallyDrop` never has a dtor.
true
} else {
// Other types might. Moreover, PhantomData doesn't
// have a dtor, but it is considered to own its
// content, so it is non-trivial. Unions can have `impl Drop`,
// and hence are non-trivial as well.
false
}
}
// The following *might* require a destructor: needs deeper inspection.
ty::Dynamic(..)
| ty::Projection(..)
| ty::Param(_)
| ty::Opaque(..)
| ty::Placeholder(..)
| ty::Infer(_)
| ty::Bound(..)
| ty::Generator(..) => false,
}
}

View file

@ -0,0 +1,97 @@
use crate::infer::canonical::OriginalQueryValues;
use crate::infer::InferCtxt;
use crate::traits::{
EvaluationResult, OverflowError, PredicateObligation, SelectionContext, TraitQueryMode,
};
pub trait InferCtxtExt<'tcx> {
fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool;
fn predicate_must_hold_considering_regions(
&self,
obligation: &PredicateObligation<'tcx>,
) -> bool;
fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'tcx>) -> bool;
fn evaluate_obligation(
&self,
obligation: &PredicateObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError>;
// Helper function that canonicalizes and runs the query. If an
// overflow results, we re-run it in the local context so we can
// report a nice error.
/*crate*/
fn evaluate_obligation_no_overflow(
&self,
obligation: &PredicateObligation<'tcx>,
) -> EvaluationResult;
}
impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
/// Evaluates whether the predicate can be satisfied (by any means)
/// in the given `ParamEnv`.
fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool {
self.evaluate_obligation_no_overflow(obligation).may_apply()
}
/// Evaluates whether the predicate can be satisfied in the given
/// `ParamEnv`, and returns `false` if not certain. However, this is
/// not entirely accurate if inference variables are involved.
///
/// This version may conservatively fail when outlives obligations
/// are required.
fn predicate_must_hold_considering_regions(
&self,
obligation: &PredicateObligation<'tcx>,
) -> bool {
self.evaluate_obligation_no_overflow(obligation).must_apply_considering_regions()
}
/// Evaluates whether the predicate can be satisfied in the given
/// `ParamEnv`, and returns `false` if not certain. However, this is
/// not entirely accurate if inference variables are involved.
///
/// This version ignores all outlives constraints.
fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'tcx>) -> bool {
self.evaluate_obligation_no_overflow(obligation).must_apply_modulo_regions()
}
/// Evaluate a given predicate, capturing overflow and propagating it back.
fn evaluate_obligation(
&self,
obligation: &PredicateObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
let mut _orig_values = OriginalQueryValues::default();
let c_pred = self
.canonicalize_query(&obligation.param_env.and(obligation.predicate), &mut _orig_values);
// Run canonical query. If overflow occurs, rerun from scratch but this time
// in standard trait query mode so that overflow is handled appropriately
// within `SelectionContext`.
self.tcx.evaluate_obligation(c_pred)
}
// Helper function that canonicalizes and runs the query. If an
// overflow results, we re-run it in the local context so we can
// report a nice error.
fn evaluate_obligation_no_overflow(
&self,
obligation: &PredicateObligation<'tcx>,
) -> EvaluationResult {
match self.evaluate_obligation(obligation) {
Ok(result) => result,
Err(OverflowError) => {
let mut selcx = SelectionContext::with_query_mode(&self, TraitQueryMode::Standard);
selcx.evaluate_root_obligation(obligation).unwrap_or_else(|r| {
span_bug!(
obligation.cause.span,
"Overflow should be caught earlier in standard query mode: {:?}, {:?}",
obligation,
r,
)
})
}
}
}
}

View file

@ -0,0 +1,3 @@
pub use rustc_middle::traits::query::{
CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult,
};

View file

@ -0,0 +1,15 @@
//! Experimental types for the trait query interface. The methods
//! defined in this module are all based on **canonicalization**,
//! which makes a canonical query by replacing unbound inference
//! variables and regions, so that results can be reused more broadly.
//! The providers for the queries defined here can be found in
//! `librustc_traits`.
pub mod dropck_outlives;
pub mod evaluate_obligation;
pub mod method_autoderef;
pub mod normalize;
pub mod outlives_bounds;
pub mod type_op;
pub use rustc_middle::traits::query::*;

View file

@ -0,0 +1,207 @@
//! Code for the 'normalization' query. This consists of a wrapper
//! which folds deeply, invoking the underlying
//! `normalize_projection_ty` query when it encounters projections.
use crate::infer::at::At;
use crate::infer::canonical::OriginalQueryValues;
use crate::infer::{InferCtxt, InferOk};
use crate::traits::error_reporting::InferCtxtExt;
use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_infer::traits::Normalized;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Ty, TyCtxt};
use super::NoSolution;
pub use rustc_middle::traits::query::NormalizationResult;
pub trait AtExt<'tcx> {
fn normalize<T>(&self, value: &T) -> Result<Normalized<'tcx, T>, NoSolution>
where
T: TypeFoldable<'tcx>;
}
impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
/// Normalize `value` in the context of the inference context,
/// yielding a resulting type, or an error if `value` cannot be
/// normalized. If you don't care about regions, you should prefer
/// `normalize_erasing_regions`, which is more efficient.
///
/// If the normalization succeeds and is unambiguous, returns back
/// the normalized value along with various outlives relations (in
/// the form of obligations that must be discharged).
///
/// N.B., this will *eventually* be the main means of
/// normalizing, but for now should be used only when we actually
/// know that normalization will succeed, since error reporting
/// and other details are still "under development".
fn normalize<T>(&self, value: &T) -> Result<Normalized<'tcx, T>, NoSolution>
where
T: TypeFoldable<'tcx>,
{
debug!(
"normalize::<{}>(value={:?}, param_env={:?})",
::std::any::type_name::<T>(),
value,
self.param_env,
);
if !value.has_projections() {
return Ok(Normalized { value: value.clone(), obligations: vec![] });
}
let mut normalizer = QueryNormalizer {
infcx: self.infcx,
cause: self.cause,
param_env: self.param_env,
obligations: vec![],
error: false,
anon_depth: 0,
};
let result = value.fold_with(&mut normalizer);
debug!(
"normalize::<{}>: result={:?} with {} obligations",
::std::any::type_name::<T>(),
result,
normalizer.obligations.len(),
);
debug!(
"normalize::<{}>: obligations={:?}",
::std::any::type_name::<T>(),
normalizer.obligations,
);
if normalizer.error {
Err(NoSolution)
} else {
Ok(Normalized { value: result, obligations: normalizer.obligations })
}
}
}
struct QueryNormalizer<'cx, 'tcx> {
infcx: &'cx InferCtxt<'cx, 'tcx>,
cause: &'cx ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
obligations: Vec<PredicateObligation<'tcx>>,
error: bool,
anon_depth: usize,
}
impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
fn tcx<'c>(&'c self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if !ty.has_projections() {
return ty;
}
let ty = ty.super_fold_with(self);
match ty.kind {
ty::Opaque(def_id, substs) => {
// Only normalize `impl Trait` after type-checking, usually in codegen.
match self.param_env.reveal() {
Reveal::UserFacing => ty,
Reveal::All => {
let recursion_limit = self.tcx().sess.recursion_limit();
if !recursion_limit.value_within_limit(self.anon_depth) {
let obligation = Obligation::with_depth(
self.cause.clone(),
recursion_limit.0,
self.param_env,
ty,
);
self.infcx.report_overflow_error(&obligation, true);
}
let generic_ty = self.tcx().type_of(def_id);
let concrete_ty = generic_ty.subst(self.tcx(), substs);
self.anon_depth += 1;
if concrete_ty == ty {
bug!(
"infinite recursion generic_ty: {:#?}, substs: {:#?}, \
concrete_ty: {:#?}, ty: {:#?}",
generic_ty,
substs,
concrete_ty,
ty
);
}
let folded_ty = ensure_sufficient_stack(|| self.fold_ty(concrete_ty));
self.anon_depth -= 1;
folded_ty
}
}
}
ty::Projection(ref data) if !data.has_escaping_bound_vars() => {
// This is kind of hacky -- we need to be able to
// handle normalization within binders because
// otherwise we wind up a need to normalize when doing
// trait matching (since you can have a trait
// obligation like `for<'a> T::B: Fn(&'a i32)`), but
// we can't normalize with bound regions in scope. So
// far now we just ignore binders but only normalize
// if all bound regions are gone (and then we still
// have to renormalize whenever we instantiate a
// binder). It would be better to normalize in a
// binding-aware fashion.
let tcx = self.infcx.tcx;
let mut orig_values = OriginalQueryValues::default();
// HACK(matthewjasper) `'static` is special-cased in selection,
// so we cannot canonicalize it.
let c_data = self
.infcx
.canonicalize_hr_query_hack(&self.param_env.and(*data), &mut orig_values);
debug!("QueryNormalizer: c_data = {:#?}", c_data);
debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
match tcx.normalize_projection_ty(c_data) {
Ok(result) => {
// We don't expect ambiguity.
if result.is_ambiguous() {
self.error = true;
return ty;
}
match self.infcx.instantiate_query_response_and_region_obligations(
self.cause,
self.param_env,
&orig_values,
&result,
) {
Ok(InferOk { value: result, obligations }) => {
debug!("QueryNormalizer: result = {:#?}", result);
debug!("QueryNormalizer: obligations = {:#?}", obligations);
self.obligations.extend(obligations);
result.normalized_ty
}
Err(_) => {
self.error = true;
ty
}
}
}
Err(NoSolution) => {
self.error = true;
ty
}
}
}
_ => ty,
}
}
fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
let constant = constant.super_fold_with(self);
constant.eval(self.infcx.tcx, self.param_env)
}
}

View file

@ -0,0 +1,95 @@
use crate::infer::canonical::OriginalQueryValues;
use crate::infer::InferCtxt;
use crate::traits::query::NoSolution;
use crate::traits::{FulfillmentContext, ObligationCause, TraitEngine};
use rustc_hir as hir;
use rustc_infer::traits::TraitEngineExt as _;
use rustc_middle::ty::{self, Ty};
use rustc_span::source_map::Span;
pub use rustc_middle::traits::query::OutlivesBound;
pub trait InferCtxtExt<'tcx> {
fn implied_outlives_bounds(
&self,
param_env: ty::ParamEnv<'tcx>,
body_id: hir::HirId,
ty: Ty<'tcx>,
span: Span,
) -> Vec<OutlivesBound<'tcx>>;
}
impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
/// Implied bounds are region relationships that we deduce
/// automatically. The idea is that (e.g.) a caller must check that a
/// function's argument types are well-formed immediately before
/// calling that fn, and hence the *callee* can assume that its
/// argument types are well-formed. This may imply certain relationships
/// between generic parameters. For example:
///
/// fn foo<'a,T>(x: &'a T)
///
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
///
/// # Parameters
///
/// - `param_env`, the where-clauses in scope
/// - `body_id`, the body-id to use when normalizing assoc types.
/// Note that this may cause outlives obligations to be injected
/// into the inference context with this body-id.
/// - `ty`, the type that we are supposed to assume is WF.
/// - `span`, a span to use when normalizing, hopefully not important,
/// might be useful if a `bug!` occurs.
fn implied_outlives_bounds(
&self,
param_env: ty::ParamEnv<'tcx>,
body_id: hir::HirId,
ty: Ty<'tcx>,
span: Span,
) -> Vec<OutlivesBound<'tcx>> {
debug!("implied_outlives_bounds(ty = {:?})", ty);
let mut orig_values = OriginalQueryValues::default();
let key = self.canonicalize_query(&param_env.and(ty), &mut orig_values);
let result = match self.tcx.implied_outlives_bounds(key) {
Ok(r) => r,
Err(NoSolution) => {
self.tcx.sess.delay_span_bug(
span,
"implied_outlives_bounds failed to solve all obligations",
);
return vec![];
}
};
assert!(result.value.is_proven());
let result = self.instantiate_query_response_and_region_obligations(
&ObligationCause::misc(span, body_id),
param_env,
&orig_values,
&result,
);
debug!("implied_outlives_bounds for {:?}: {:#?}", ty, result);
let result = match result {
Ok(v) => v,
Err(_) => {
self.tcx.sess.delay_span_bug(span, "implied_outlives_bounds failed to instantiate");
return vec![];
}
};
// Instantiation may have produced new inference variables and constraints on those
// variables. Process these constraints.
let mut fulfill_cx = FulfillmentContext::new();
fulfill_cx.register_predicate_obligations(self, result.obligations);
if fulfill_cx.select_all_or_error(self).is_err() {
self.tcx.sess.delay_span_bug(
span,
"implied_outlives_bounds failed to solve obligations from instantiation",
);
}
result.value
}
}

View file

@ -0,0 +1,23 @@
use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse};
use crate::traits::query::Fallible;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
pub use rustc_middle::traits::query::type_op::AscribeUserType;
impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> {
type QueryResponse = ();
fn try_fast_path(
_tcx: TyCtxt<'tcx>,
_key: &ParamEnvAnd<'tcx, Self>,
) -> Option<Self::QueryResponse> {
None
}
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> {
tcx.type_op_ascribe_user_type(canonicalized)
}
}

View file

@ -0,0 +1,108 @@
use crate::infer::{InferCtxt, InferOk};
use crate::traits::query::Fallible;
use std::fmt;
use crate::infer::canonical::query_response;
use crate::infer::canonical::QueryRegionConstraints;
use crate::traits::engine::TraitEngineExt as _;
use crate::traits::{ObligationCause, TraitEngine};
use rustc_infer::traits::TraitEngineExt as _;
use rustc_span::source_map::DUMMY_SP;
use std::rc::Rc;
pub struct CustomTypeOp<F, G> {
closure: F,
description: G,
}
impl<F, G> CustomTypeOp<F, G> {
pub fn new<'tcx, R>(closure: F, description: G) -> Self
where
F: FnOnce(&InferCtxt<'_, 'tcx>) -> Fallible<InferOk<'tcx, R>>,
G: Fn() -> String,
{
CustomTypeOp { closure, description }
}
}
impl<'tcx, F, R, G> super::TypeOp<'tcx> for CustomTypeOp<F, G>
where
F: for<'a, 'cx> FnOnce(&'a InferCtxt<'cx, 'tcx>) -> Fallible<InferOk<'tcx, R>>,
G: Fn() -> String,
{
type Output = R;
/// Processes the operation and all resulting obligations,
/// returning the final result along with any region constraints
/// (they will be given over to the NLL region solver).
fn fully_perform(
self,
infcx: &InferCtxt<'_, 'tcx>,
) -> Fallible<(Self::Output, Option<Rc<QueryRegionConstraints<'tcx>>>)> {
if cfg!(debug_assertions) {
info!("fully_perform({:?})", self);
}
scrape_region_constraints(infcx, || Ok((self.closure)(infcx)?))
}
}
impl<F, G> fmt::Debug for CustomTypeOp<F, G>
where
G: Fn() -> String,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", (self.description)())
}
}
/// Executes `op` and then scrapes out all the "old style" region
/// constraints that result, creating query-region-constraints.
fn scrape_region_constraints<'tcx, R>(
infcx: &InferCtxt<'_, 'tcx>,
op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>,
) -> Fallible<(R, Option<Rc<QueryRegionConstraints<'tcx>>>)> {
let mut fulfill_cx = TraitEngine::new(infcx.tcx);
let dummy_body_id = ObligationCause::dummy().body_id;
// During NLL, we expect that nobody will register region
// obligations **except** as part of a custom type op (and, at the
// end of each custom type op, we scrape out the region
// obligations that resulted). So this vector should be empty on
// entry.
let pre_obligations = infcx.take_registered_region_obligations();
assert!(
pre_obligations.is_empty(),
"scrape_region_constraints: incoming region obligations = {:#?}",
pre_obligations,
);
let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?;
debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id));
fulfill_cx.register_predicate_obligations(infcx, obligations);
if let Err(e) = fulfill_cx.select_all_or_error(infcx) {
infcx.tcx.sess.diagnostic().delay_span_bug(
DUMMY_SP,
&format!("errors selecting obligation during MIR typeck: {:?}", e),
);
}
let region_obligations = infcx.take_registered_region_obligations();
let region_constraint_data = infcx.take_and_reset_region_constraints();
let region_constraints = query_response::make_query_region_constraints(
infcx.tcx,
region_obligations
.iter()
.map(|(_, r_o)| (r_o.sup_type, r_o.sub_region))
.map(|(ty, r)| (infcx.resolve_vars_if_possible(&ty), r)),
&region_constraint_data,
);
if region_constraints.is_empty() {
Ok((value, None))
} else {
Ok((value, Some(Rc::new(region_constraints))))
}
}

View file

@ -0,0 +1,23 @@
use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse};
use crate::traits::query::Fallible;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
pub use rustc_middle::traits::query::type_op::Eq;
impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> {
type QueryResponse = ();
fn try_fast_path(
_tcx: TyCtxt<'tcx>,
key: &ParamEnvAnd<'tcx, Eq<'tcx>>,
) -> Option<Self::QueryResponse> {
if key.value.a == key.value.b { Some(()) } else { None }
}
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> {
tcx.type_op_eq(canonicalized)
}
}

View file

@ -0,0 +1,41 @@
use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse};
use crate::traits::query::outlives_bounds::OutlivesBound;
use crate::traits::query::Fallible;
use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt};
#[derive(Clone, Debug, HashStable, TypeFoldable, Lift)]
pub struct ImpliedOutlivesBounds<'tcx> {
pub ty: Ty<'tcx>,
}
impl<'tcx> ImpliedOutlivesBounds<'tcx> {
pub fn new(ty: Ty<'tcx>) -> Self {
ImpliedOutlivesBounds { ty }
}
}
impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> {
type QueryResponse = Vec<OutlivesBound<'tcx>>;
fn try_fast_path(
_tcx: TyCtxt<'tcx>,
_key: &ParamEnvAnd<'tcx, Self>,
) -> Option<Self::QueryResponse> {
None
}
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>> {
// FIXME this `unchecked_map` is only necessary because the
// query is defined as taking a `ParamEnvAnd<Ty>`; it should
// take a `ImpliedOutlivesBounds` instead
let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| {
let ImpliedOutlivesBounds { ty } = value;
param_env.and(ty)
});
tcx.implied_outlives_bounds(canonicalized)
}
}

View file

@ -0,0 +1,136 @@
use crate::infer::canonical::{
Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, QueryRegionConstraints,
};
use crate::infer::{InferCtxt, InferOk};
use crate::traits::query::Fallible;
use crate::traits::ObligationCause;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
use std::fmt;
use std::rc::Rc;
pub mod ascribe_user_type;
pub mod custom;
pub mod eq;
pub mod implied_outlives_bounds;
pub mod normalize;
pub mod outlives;
pub mod prove_predicate;
use self::prove_predicate::ProvePredicate;
pub mod subtype;
pub use rustc_middle::traits::query::type_op::*;
/// "Type ops" are used in NLL to perform some particular action and
/// extract out the resulting region constraints (or an error if it
/// cannot be completed).
pub trait TypeOp<'tcx>: Sized + fmt::Debug {
type Output;
/// Processes the operation and all resulting obligations,
/// returning the final result along with any region constraints
/// (they will be given over to the NLL region solver).
fn fully_perform(
self,
infcx: &InferCtxt<'_, 'tcx>,
) -> Fallible<(Self::Output, Option<Rc<QueryRegionConstraints<'tcx>>>)>;
}
/// "Query type ops" are type ops that are implemented using a
/// [canonical query][c]. The `Self` type here contains the kernel of
/// information needed to do the operation -- `TypeOp` is actually
/// implemented for `ParamEnvAnd<Self>`, since we always need to bring
/// along a parameter environment as well. For query type-ops, we will
/// first canonicalize the key and then invoke the query on the tcx,
/// which produces the resulting query region constraints.
///
/// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
pub trait QueryTypeOp<'tcx>: fmt::Debug + Sized + TypeFoldable<'tcx> + 'tcx {
type QueryResponse: TypeFoldable<'tcx>;
/// Give query the option for a simple fast path that never
/// actually hits the tcx cache lookup etc. Return `Some(r)` with
/// a final result or `None` to do the full path.
fn try_fast_path(
tcx: TyCtxt<'tcx>,
key: &ParamEnvAnd<'tcx, Self>,
) -> Option<Self::QueryResponse>;
/// Performs the actual query with the canonicalized key -- the
/// real work happens here. This method is not given an `infcx`
/// because it shouldn't need one -- and if it had access to one,
/// it might do things like invoke `sub_regions`, which would be
/// bad, because it would create subregion relationships that are
/// not captured in the return value.
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>>;
fn fully_perform_into(
query_key: ParamEnvAnd<'tcx, Self>,
infcx: &InferCtxt<'_, 'tcx>,
output_query_region_constraints: &mut QueryRegionConstraints<'tcx>,
) -> Fallible<Self::QueryResponse> {
if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) {
return Ok(result);
}
// FIXME(#33684) -- We need to use
// `canonicalize_hr_query_hack` here because of things
// like the subtype query, which go awry around
// `'static` otherwise.
let mut canonical_var_values = OriginalQueryValues::default();
let canonical_self =
infcx.canonicalize_hr_query_hack(&query_key, &mut canonical_var_values);
let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?;
let param_env = query_key.param_env;
let InferOk { value, obligations } = infcx
.instantiate_nll_query_response_and_region_obligations(
&ObligationCause::dummy(),
param_env,
&canonical_var_values,
canonical_result,
output_query_region_constraints,
)?;
// Typically, instantiating NLL query results does not
// create obligations. However, in some cases there
// are unresolved type variables, and unify them *can*
// create obligations. In that case, we have to go
// fulfill them. We do this via a (recursive) query.
for obligation in obligations {
let () = ProvePredicate::fully_perform_into(
obligation.param_env.and(ProvePredicate::new(obligation.predicate)),
infcx,
output_query_region_constraints,
)?;
}
Ok(value)
}
}
impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q>
where
Q: QueryTypeOp<'tcx>,
{
type Output = Q::QueryResponse;
fn fully_perform(
self,
infcx: &InferCtxt<'_, 'tcx>,
) -> Fallible<(Self::Output, Option<Rc<QueryRegionConstraints<'tcx>>>)> {
let mut region_constraints = QueryRegionConstraints::default();
let r = Q::fully_perform_into(self, infcx, &mut region_constraints)?;
// Promote the final query-region-constraints into a
// (optional) ref-counted vector:
let opt_qrc =
if region_constraints.is_empty() { None } else { Some(Rc::new(region_constraints)) };
Ok((r, opt_qrc))
}
}

View file

@ -0,0 +1,68 @@
use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse};
use crate::traits::query::Fallible;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt};
use std::fmt;
pub use rustc_middle::traits::query::type_op::Normalize;
impl<'tcx, T> super::QueryTypeOp<'tcx> for Normalize<T>
where
T: Normalizable<'tcx> + 'tcx,
{
type QueryResponse = T;
fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<T> {
if !key.value.value.has_projections() { Some(key.value.value) } else { None }
}
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>> {
T::type_op_method(tcx, canonicalized)
}
}
pub trait Normalizable<'tcx>: fmt::Debug + TypeFoldable<'tcx> + Lift<'tcx> + Copy {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>>;
}
impl Normalizable<'tcx> for Ty<'tcx> {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> {
tcx.type_op_normalize_ty(canonicalized)
}
}
impl Normalizable<'tcx> for ty::Predicate<'tcx> {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> {
tcx.type_op_normalize_predicate(canonicalized)
}
}
impl Normalizable<'tcx> for ty::PolyFnSig<'tcx> {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> {
tcx.type_op_normalize_poly_fn_sig(canonicalized)
}
}
impl Normalizable<'tcx> for ty::FnSig<'tcx> {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> {
tcx.type_op_normalize_fn_sig(canonicalized)
}
}

View file

@ -0,0 +1,55 @@
use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse};
use crate::traits::query::dropck_outlives::{trivial_dropck_outlives, DropckOutlivesResult};
use crate::traits::query::Fallible;
use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt};
#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, Lift)]
pub struct DropckOutlives<'tcx> {
dropped_ty: Ty<'tcx>,
}
impl<'tcx> DropckOutlives<'tcx> {
pub fn new(dropped_ty: Ty<'tcx>) -> Self {
DropckOutlives { dropped_ty }
}
}
impl super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> {
type QueryResponse = DropckOutlivesResult<'tcx>;
fn try_fast_path(
tcx: TyCtxt<'tcx>,
key: &ParamEnvAnd<'tcx, Self>,
) -> Option<Self::QueryResponse> {
if trivial_dropck_outlives(tcx, key.value.dropped_ty) {
Some(DropckOutlivesResult::default())
} else {
None
}
}
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>> {
// Subtle: note that we are not invoking
// `infcx.at(...).dropck_outlives(...)` here, but rather the
// underlying `dropck_outlives` query. This same underlying
// query is also used by the
// `infcx.at(...).dropck_outlives(...)` fn. Avoiding the
// wrapper means we don't need an infcx in this code, which is
// good because the interface doesn't give us one (so that we
// know we are not registering any subregion relations or
// other things).
// FIXME convert to the type expected by the `dropck_outlives`
// query. This should eventually be fixed by changing the
// *underlying query*.
let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| {
let DropckOutlives { dropped_ty } = value;
param_env.and(dropped_ty)
});
tcx.dropck_outlives(canonicalized)
}
}

View file

@ -0,0 +1,37 @@
use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse};
use crate::traits::query::Fallible;
use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt};
pub use rustc_middle::traits::query::type_op::ProvePredicate;
impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
type QueryResponse = ();
fn try_fast_path(
tcx: TyCtxt<'tcx>,
key: &ParamEnvAnd<'tcx, Self>,
) -> Option<Self::QueryResponse> {
// Proving Sized, very often on "obviously sized" types like
// `&T`, accounts for about 60% percentage of the predicates
// we have to prove. No need to canonicalize and all that for
// such cases.
if let ty::PredicateAtom::Trait(trait_ref, _) = key.value.predicate.skip_binders() {
if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
if trait_ref.def_id() == sized_def_id {
if trait_ref.self_ty().is_trivially_sized(tcx) {
return Some(());
}
}
}
}
None
}
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> {
tcx.type_op_prove_predicate(canonicalized)
}
}

View file

@ -0,0 +1,20 @@
use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse};
use crate::traits::query::Fallible;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
pub use rustc_middle::traits::query::type_op::Subtype;
impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> {
type QueryResponse = ();
fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<()> {
if key.value.sub == key.value.sup { Some(()) } else { None }
}
fn perform_query(
tcx: TyCtxt<'tcx>,
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>,
) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> {
tcx.type_op_subtype(canonicalized)
}
}

View file

@ -0,0 +1,635 @@
//! Candidate assembly.
//!
//! The selection process begins by examining all in-scope impls,
//! caller obligations, and so forth and assembling a list of
//! candidates. See the [rustc dev guide] for more details.
//!
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
use rustc_hir as hir;
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
use rustc_middle::ty::{self, TypeFoldable};
use rustc_target::spec::abi::Abi;
use crate::traits::{util, SelectionResult};
use super::BuiltinImplConditions;
use super::SelectionCandidate::{self, *};
use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack};
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
pub(super) fn candidate_from_obligation<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
// Watch out for overflow. This intentionally bypasses (and does
// not update) the cache.
self.check_recursion_limit(&stack.obligation, &stack.obligation)?;
// Check the cache. Note that we freshen the trait-ref
// separately rather than using `stack.fresh_trait_ref` --
// this is because we want the unbound variables to be
// replaced with fresh types starting from index 0.
let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate);
debug!(
"candidate_from_obligation(cache_fresh_trait_pred={:?}, obligation={:?})",
cache_fresh_trait_pred, stack
);
debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars());
if let Some(c) =
self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred)
{
debug!("CACHE HIT: SELECT({:?})={:?}", cache_fresh_trait_pred, c);
return c;
}
// If no match, compute result and insert into cache.
//
// FIXME(nikomatsakis) -- this cache is not taking into
// account cycles that may have occurred in forming the
// candidate. I don't know of any specific problems that
// result but it seems awfully suspicious.
let (candidate, dep_node) =
self.in_task(|this| this.candidate_from_obligation_no_cache(stack));
debug!("CACHE MISS: SELECT({:?})={:?}", cache_fresh_trait_pred, candidate);
self.insert_candidate_cache(
stack.obligation.param_env,
cache_fresh_trait_pred,
dep_node,
candidate.clone(),
);
candidate
}
pub(super) fn assemble_candidates<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
) -> Result<SelectionCandidateSet<'tcx>, SelectionError<'tcx>> {
let TraitObligationStack { obligation, .. } = *stack;
let obligation = &Obligation {
param_env: obligation.param_env,
cause: obligation.cause.clone(),
recursion_depth: obligation.recursion_depth,
predicate: self.infcx().resolve_vars_if_possible(&obligation.predicate),
};
if obligation.predicate.skip_binder().self_ty().is_ty_var() {
// Self is a type variable (e.g., `_: AsRef<str>`).
//
// This is somewhat problematic, as the current scheme can't really
// handle it turning to be a projection. This does end up as truly
// ambiguous in most cases anyway.
//
// Take the fast path out - this also improves
// performance by preventing assemble_candidates_from_impls from
// matching every impl for this trait.
return Ok(SelectionCandidateSet { vec: vec![], ambiguous: true });
}
let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false };
self.assemble_candidates_for_trait_alias(obligation, &mut candidates)?;
// Other bounds. Consider both in-scope bounds from fn decl
// and applicable impls. There is a certain set of precedence rules here.
let def_id = obligation.predicate.def_id();
let lang_items = self.tcx().lang_items();
if lang_items.copy_trait() == Some(def_id) {
debug!("obligation self ty is {:?}", obligation.predicate.skip_binder().self_ty());
// User-defined copy impls are permitted, but only for
// structs and enums.
self.assemble_candidates_from_impls(obligation, &mut candidates)?;
// For other types, we'll use the builtin rules.
let copy_conditions = self.copy_clone_conditions(obligation);
self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates)?;
} else if lang_items.discriminant_kind_trait() == Some(def_id) {
// `DiscriminantKind` is automatically implemented for every type.
candidates.vec.push(DiscriminantKindCandidate);
} else if lang_items.sized_trait() == Some(def_id) {
// Sized is never implementable by end-users, it is
// always automatically computed.
let sized_conditions = self.sized_conditions(obligation);
self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?;
} else if lang_items.unsize_trait() == Some(def_id) {
self.assemble_candidates_for_unsizing(obligation, &mut candidates);
} else {
if lang_items.clone_trait() == Some(def_id) {
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
// for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone`
// types have builtin support for `Clone`.
let clone_conditions = self.copy_clone_conditions(obligation);
self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?;
}
self.assemble_generator_candidates(obligation, &mut candidates)?;
self.assemble_closure_candidates(obligation, &mut candidates)?;
self.assemble_fn_pointer_candidates(obligation, &mut candidates)?;
self.assemble_candidates_from_impls(obligation, &mut candidates)?;
self.assemble_candidates_from_object_ty(obligation, &mut candidates);
}
self.assemble_candidates_from_projected_tys(obligation, &mut candidates);
self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?;
// Auto implementations have lower priority, so we only
// consider triggering a default if there is no other impl that can apply.
if candidates.vec.is_empty() {
self.assemble_candidates_from_auto_impls(obligation, &mut candidates)?;
}
debug!("candidate list size: {}", candidates.vec.len());
Ok(candidates)
}
fn assemble_candidates_from_projected_tys(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
debug!("assemble_candidates_for_projected_tys({:?})", obligation);
// Before we go into the whole placeholder thing, just
// quickly check if the self-type is a projection at all.
match obligation.predicate.skip_binder().trait_ref.self_ty().kind {
ty::Projection(_) | ty::Opaque(..) => {}
ty::Infer(ty::TyVar(_)) => {
span_bug!(
obligation.cause.span,
"Self=_ should have been handled by assemble_candidates"
);
}
_ => return,
}
let result = self
.infcx
.probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
if result {
candidates.vec.push(ProjectionCandidate);
}
}
/// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller
/// supplied to find out whether it is listed among them.
///
/// Never affects the inference environment.
fn assemble_candidates_from_caller_bounds<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
debug!("assemble_candidates_from_caller_bounds({:?})", stack.obligation);
let all_bounds = stack
.obligation
.param_env
.caller_bounds()
.iter()
.filter_map(|o| o.to_opt_poly_trait_ref());
// Micro-optimization: filter out predicates relating to different traits.
let matching_bounds =
all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id());
// Keep only those bounds which may apply, and propagate overflow if it occurs.
let mut param_candidates = vec![];
for bound in matching_bounds {
let wc = self.evaluate_where_clause(stack, bound)?;
if wc.may_apply() {
param_candidates.push(ParamCandidate(bound));
}
}
candidates.vec.extend(param_candidates);
Ok(())
}
fn assemble_generator_candidates(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
if self.tcx().lang_items().gen_trait() != Some(obligation.predicate.def_id()) {
return Ok(());
}
// Okay to skip binder because the substs on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
let self_ty = obligation.self_ty().skip_binder();
match self_ty.kind {
ty::Generator(..) => {
debug!(
"assemble_generator_candidates: self_ty={:?} obligation={:?}",
self_ty, obligation
);
candidates.vec.push(GeneratorCandidate);
}
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_generator_candidates: ambiguous self-type");
candidates.ambiguous = true;
}
_ => {}
}
Ok(())
}
/// Checks for the artificial impl that the compiler will create for an obligation like `X :
/// FnMut<..>` where `X` is a closure type.
///
/// Note: the type parameters on a closure candidate are modeled as *output* type
/// parameters and hence do not affect whether this trait is a match or not. They will be
/// unified during the confirmation step.
fn assemble_closure_candidates(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
let kind = match self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()) {
Some(k) => k,
None => {
return Ok(());
}
};
// Okay to skip binder because the substs on closure types never
// touch bound regions, they just capture the in-scope
// type/region parameters
match obligation.self_ty().skip_binder().kind {
ty::Closure(_, closure_substs) => {
debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation);
match self.infcx.closure_kind(closure_substs) {
Some(closure_kind) => {
debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind);
if closure_kind.extends(kind) {
candidates.vec.push(ClosureCandidate);
}
}
None => {
debug!("assemble_unboxed_candidates: closure_kind not yet known");
candidates.vec.push(ClosureCandidate);
}
}
}
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_unboxed_closure_candidates: ambiguous self-type");
candidates.ambiguous = true;
}
_ => {}
}
Ok(())
}
/// Implements one of the `Fn()` family for a fn pointer.
fn assemble_fn_pointer_candidates(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
// We provide impl of all fn traits for fn pointers.
if self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()).is_none() {
return Ok(());
}
// Okay to skip binder because what we are inspecting doesn't involve bound regions.
let self_ty = obligation.self_ty().skip_binder();
match self_ty.kind {
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_fn_pointer_candidates: ambiguous self-type");
candidates.ambiguous = true; // Could wind up being a fn() type.
}
// Provide an impl, but only for suitable `fn` pointers.
ty::FnPtr(_) => {
if let ty::FnSig {
unsafety: hir::Unsafety::Normal,
abi: Abi::Rust,
c_variadic: false,
..
} = self_ty.fn_sig(self.tcx()).skip_binder()
{
candidates.vec.push(FnPointerCandidate);
}
}
// Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
ty::FnDef(def_id, _) => {
if let ty::FnSig {
unsafety: hir::Unsafety::Normal,
abi: Abi::Rust,
c_variadic: false,
..
} = self_ty.fn_sig(self.tcx()).skip_binder()
{
if self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() {
candidates.vec.push(FnPointerCandidate);
}
}
}
_ => {}
}
Ok(())
}
/// Searches for impls that might apply to `obligation`.
fn assemble_candidates_from_impls(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
debug!("assemble_candidates_from_impls(obligation={:?})", obligation);
// Essentially any user-written impl will match with an error type,
// so creating `ImplCandidates` isn't useful. However, we might
// end up finding a candidate elsewhere (e.g. a `BuiltinCandidate` for `Sized)
// This helps us avoid overflow: see issue #72839
// Since compilation is already guaranteed to fail, this is just
// to try to show the 'nicest' possible errors to the user.
if obligation.references_error() {
return Ok(());
}
self.tcx().for_each_relevant_impl(
obligation.predicate.def_id(),
obligation.predicate.skip_binder().trait_ref.self_ty(),
|impl_def_id| {
self.infcx.probe(|_| {
if let Ok(_substs) = self.match_impl(impl_def_id, obligation) {
candidates.vec.push(ImplCandidate(impl_def_id));
}
});
},
);
Ok(())
}
fn assemble_candidates_from_auto_impls(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
// Okay to skip binder here because the tests we do below do not involve bound regions.
let self_ty = obligation.self_ty().skip_binder();
debug!("assemble_candidates_from_auto_impls(self_ty={:?})", self_ty);
let def_id = obligation.predicate.def_id();
if self.tcx().trait_is_auto(def_id) {
match self_ty.kind {
ty::Dynamic(..) => {
// For object types, we don't know what the closed
// over types are. This means we conservatively
// say nothing; a candidate may be added by
// `assemble_candidates_from_object_ty`.
}
ty::Foreign(..) => {
// Since the contents of foreign types is unknown,
// we don't add any `..` impl. Default traits could
// still be provided by a manual implementation for
// this trait and type.
}
ty::Param(..) | ty::Projection(..) => {
// In these cases, we don't know what the actual
// type is. Therefore, we cannot break it down
// into its constituent types. So we don't
// consider the `..` impl but instead just add no
// candidates: this means that typeck will only
// succeed if there is another reason to believe
// that this obligation holds. That could be a
// where-clause or, in the case of an object type,
// it could be that the object type lists the
// trait (e.g., `Foo+Send : Send`). See
// `compile-fail/typeck-default-trait-impl-send-param.rs`
// for an example of a test case that exercises
// this path.
}
ty::Infer(ty::TyVar(_)) => {
// The auto impl might apply; we don't know.
candidates.ambiguous = true;
}
ty::Generator(_, _, movability)
if self.tcx().lang_items().unpin_trait() == Some(def_id) =>
{
match movability {
hir::Movability::Static => {
// Immovable generators are never `Unpin`, so
// suppress the normal auto-impl candidate for it.
}
hir::Movability::Movable => {
// Movable generators are always `Unpin`, so add an
// unconditional builtin candidate.
candidates.vec.push(BuiltinCandidate { has_nested: false });
}
}
}
_ => candidates.vec.push(AutoImplCandidate(def_id)),
}
}
Ok(())
}
/// Searches for impls that might apply to `obligation`.
fn assemble_candidates_from_object_ty(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
debug!(
"assemble_candidates_from_object_ty(self_ty={:?})",
obligation.self_ty().skip_binder()
);
self.infcx.probe(|_snapshot| {
// The code below doesn't care about regions, and the
// self-ty here doesn't escape this probe, so just erase
// any LBR.
let self_ty = self.tcx().erase_late_bound_regions(&obligation.self_ty());
let poly_trait_ref = match self_ty.kind {
ty::Dynamic(ref data, ..) => {
if data.auto_traits().any(|did| did == obligation.predicate.def_id()) {
debug!(
"assemble_candidates_from_object_ty: matched builtin bound, \
pushing candidate"
);
candidates.vec.push(BuiltinObjectCandidate);
return;
}
if let Some(principal) = data.principal() {
if !self.infcx.tcx.features().object_safe_for_dispatch {
principal.with_self_ty(self.tcx(), self_ty)
} else if self.tcx().is_object_safe(principal.def_id()) {
principal.with_self_ty(self.tcx(), self_ty)
} else {
return;
}
} else {
// Only auto trait bounds exist.
return;
}
}
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_candidates_from_object_ty: ambiguous");
candidates.ambiguous = true; // could wind up being an object type
return;
}
_ => return,
};
debug!("assemble_candidates_from_object_ty: poly_trait_ref={:?}", poly_trait_ref);
// Count only those upcast versions that match the trait-ref
// we are looking for. Specifically, do not only check for the
// correct trait, but also the correct type parameters.
// For example, we may be trying to upcast `Foo` to `Bar<i32>`,
// but `Foo` is declared as `trait Foo: Bar<u32>`.
let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref)
.filter(|upcast_trait_ref| {
self.infcx
.probe(|_| self.match_poly_trait_ref(obligation, *upcast_trait_ref).is_ok())
})
.count();
if upcast_trait_refs > 1 {
// Can be upcast in many ways; need more type information.
candidates.ambiguous = true;
} else if upcast_trait_refs == 1 {
candidates.vec.push(ObjectCandidate);
}
})
}
/// Searches for unsizing that might apply to `obligation`.
fn assemble_candidates_for_unsizing(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// We currently never consider higher-ranked obligations e.g.
// `for<'a> &'a T: Unsize<Trait+'a>` to be implemented. This is not
// because they are a priori invalid, and we could potentially add support
// for them later, it's just that there isn't really a strong need for it.
// A `T: Unsize<U>` obligation is always used as part of a `T: CoerceUnsize<U>`
// impl, and those are generally applied to concrete types.
//
// That said, one might try to write a fn with a where clause like
// for<'a> Foo<'a, T>: Unsize<Foo<'a, Trait>>
// where the `'a` is kind of orthogonal to the relevant part of the `Unsize`.
// Still, you'd be more likely to write that where clause as
// T: Trait
// so it seems ok if we (conservatively) fail to accept that `Unsize`
// obligation above. Should be possible to extend this in the future.
let source = match obligation.self_ty().no_bound_vars() {
Some(t) => t,
None => {
// Don't add any candidates if there are bound regions.
return;
}
};
let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1);
debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})", source, target);
let may_apply = match (&source.kind, &target.kind) {
// Trait+Kx+'a -> Trait+Ky+'b (upcasts).
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
// Upcasts permit two things:
//
// 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
// 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b`
//
// Note that neither of these changes requires any
// change at runtime. Eventually this will be
// generalized.
//
// We always upcast when we can because of reason
// #2 (region bounds).
data_a.principal_def_id() == data_b.principal_def_id()
&& data_b
.auto_traits()
// All of a's auto traits need to be in b's auto traits.
.all(|b| data_a.auto_traits().any(|a| a == b))
}
// `T` -> `Trait`
(_, &ty::Dynamic(..)) => true,
// Ambiguous handling is below `T` -> `Trait`, because inference
// variables can still implement `Unsize<Trait>` and nested
// obligations will have the final say (likely deferred).
(&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => {
debug!("assemble_candidates_for_unsizing: ambiguous");
candidates.ambiguous = true;
false
}
// `[T; n]` -> `[T]`
(&ty::Array(..), &ty::Slice(_)) => true,
// `Struct<T>` -> `Struct<U>`
(&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => {
def_id_a == def_id_b
}
// `(.., T)` -> `(.., U)`
(&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => tys_a.len() == tys_b.len(),
_ => false,
};
if may_apply {
candidates.vec.push(BuiltinUnsizeCandidate);
}
}
fn assemble_candidates_for_trait_alias(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
// Okay to skip binder here because the tests we do below do not involve bound regions.
let self_ty = obligation.self_ty().skip_binder();
debug!("assemble_candidates_for_trait_alias(self_ty={:?})", self_ty);
let def_id = obligation.predicate.def_id();
if self.tcx().is_trait_alias(def_id) {
candidates.vec.push(TraitAliasCandidate(def_id));
}
Ok(())
}
/// Assembles the trait which are built-in to the language itself:
/// `Copy`, `Clone` and `Sized`.
fn assemble_builtin_bound_candidates(
&mut self,
conditions: BuiltinImplConditions<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) -> Result<(), SelectionError<'tcx>> {
match conditions {
BuiltinImplConditions::Where(nested) => {
debug!("builtin_bound: nested={:?}", nested);
candidates
.vec
.push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() });
}
BuiltinImplConditions::None => {}
BuiltinImplConditions::Ambiguous => {
debug!("assemble_builtin_bound_candidates: ambiguous builtin");
candidates.ambiguous = true;
}
}
Ok(())
}
}

View file

@ -0,0 +1,810 @@
//! Confirmation.
//!
//! Confirmation unifies the output type parameters of the trait
//! with the values found in the obligation, possibly yielding a
//! type error. See the [rustc dev guide] for more details.
//!
//! [rustc dev guide]:
//! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::lang_items::LangItem;
use rustc_index::bit_set::GrowableBitSet;
use rustc_infer::infer::InferOk;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{ToPolyTraitRef, ToPredicate, WithConstness};
use rustc_span::def_id::DefId;
use crate::traits::project::{self, normalize_with_depth};
use crate::traits::select::TraitObligationExt;
use crate::traits::util;
use crate::traits::util::{closure_trait_ref_and_return_type, predicate_for_trait_def};
use crate::traits::Normalized;
use crate::traits::OutputTypeParameterMismatch;
use crate::traits::Selection;
use crate::traits::TraitNotObjectSafe;
use crate::traits::{BuiltinDerivedObligation, ImplDerivedObligation};
use crate::traits::{
ImplSourceAutoImpl, ImplSourceBuiltin, ImplSourceClosure, ImplSourceDiscriminantKind,
ImplSourceFnPointer, ImplSourceGenerator, ImplSourceObject, ImplSourceParam,
ImplSourceTraitAlias, ImplSourceUserDefined,
};
use crate::traits::{
ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData,
ImplSourceDiscriminantKindData, ImplSourceFnPointerData, ImplSourceGeneratorData,
ImplSourceObjectData, ImplSourceTraitAliasData, ImplSourceUserDefinedData,
};
use crate::traits::{ObjectCastObligation, PredicateObligation, TraitObligation};
use crate::traits::{Obligation, ObligationCause};
use crate::traits::{SelectionError, Unimplemented};
use super::BuiltinImplConditions;
use super::SelectionCandidate::{self, *};
use super::SelectionContext;
use std::iter;
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
pub(super) fn confirm_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
candidate: SelectionCandidate<'tcx>,
) -> Result<Selection<'tcx>, SelectionError<'tcx>> {
debug!("confirm_candidate({:?}, {:?})", obligation, candidate);
match candidate {
BuiltinCandidate { has_nested } => {
let data = self.confirm_builtin_candidate(obligation, has_nested);
Ok(ImplSourceBuiltin(data))
}
ParamCandidate(param) => {
let obligations = self.confirm_param_candidate(obligation, param);
Ok(ImplSourceParam(obligations))
}
ImplCandidate(impl_def_id) => {
Ok(ImplSourceUserDefined(self.confirm_impl_candidate(obligation, impl_def_id)))
}
AutoImplCandidate(trait_def_id) => {
let data = self.confirm_auto_impl_candidate(obligation, trait_def_id);
Ok(ImplSourceAutoImpl(data))
}
ProjectionCandidate => {
self.confirm_projection_candidate(obligation);
Ok(ImplSourceParam(Vec::new()))
}
ClosureCandidate => {
let vtable_closure = self.confirm_closure_candidate(obligation)?;
Ok(ImplSourceClosure(vtable_closure))
}
GeneratorCandidate => {
let vtable_generator = self.confirm_generator_candidate(obligation)?;
Ok(ImplSourceGenerator(vtable_generator))
}
FnPointerCandidate => {
let data = self.confirm_fn_pointer_candidate(obligation)?;
Ok(ImplSourceFnPointer(data))
}
DiscriminantKindCandidate => {
Ok(ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData))
}
TraitAliasCandidate(alias_def_id) => {
let data = self.confirm_trait_alias_candidate(obligation, alias_def_id);
Ok(ImplSourceTraitAlias(data))
}
ObjectCandidate => {
let data = self.confirm_object_candidate(obligation);
Ok(ImplSourceObject(data))
}
BuiltinObjectCandidate => {
// This indicates something like `Trait + Send: Send`. In this case, we know that
// this holds because that's what the object type is telling us, and there's really
// no additional obligations to prove and no types in particular to unify, etc.
Ok(ImplSourceParam(Vec::new()))
}
BuiltinUnsizeCandidate => {
let data = self.confirm_builtin_unsize_candidate(obligation)?;
Ok(ImplSourceBuiltin(data))
}
}
}
fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) {
self.infcx.commit_unconditionally(|_| {
let result = self.match_projection_obligation_against_definition_bounds(obligation);
assert!(result);
})
}
fn confirm_param_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
param: ty::PolyTraitRef<'tcx>,
) -> Vec<PredicateObligation<'tcx>> {
debug!("confirm_param_candidate({:?},{:?})", obligation, param);
// During evaluation, we already checked that this
// where-clause trait-ref could be unified with the obligation
// trait-ref. Repeat that unification now without any
// transactional boundary; it should not fail.
match self.match_where_clause_trait_ref(obligation, param) {
Ok(obligations) => obligations,
Err(()) => {
bug!(
"Where clause `{:?}` was applicable to `{:?}` but now is not",
param,
obligation
);
}
}
}
fn confirm_builtin_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
has_nested: bool,
) -> ImplSourceBuiltinData<PredicateObligation<'tcx>> {
debug!("confirm_builtin_candidate({:?}, {:?})", obligation, has_nested);
let lang_items = self.tcx().lang_items();
let obligations = if has_nested {
let trait_def = obligation.predicate.def_id();
let conditions = if Some(trait_def) == lang_items.sized_trait() {
self.sized_conditions(obligation)
} else if Some(trait_def) == lang_items.copy_trait() {
self.copy_clone_conditions(obligation)
} else if Some(trait_def) == lang_items.clone_trait() {
self.copy_clone_conditions(obligation)
} else {
bug!("unexpected builtin trait {:?}", trait_def)
};
let nested = match conditions {
BuiltinImplConditions::Where(nested) => nested,
_ => bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation),
};
let cause = obligation.derived_cause(BuiltinDerivedObligation);
ensure_sufficient_stack(|| {
self.collect_predicates_for_types(
obligation.param_env,
cause,
obligation.recursion_depth + 1,
trait_def,
nested,
)
})
} else {
vec![]
};
debug!("confirm_builtin_candidate: obligations={:?}", obligations);
ImplSourceBuiltinData { nested: obligations }
}
/// This handles the case where a `auto trait Foo` impl is being used.
/// The idea is that the impl applies to `X : Foo` if the following conditions are met:
///
/// 1. For each constituent type `Y` in `X`, `Y : Foo` holds
/// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds.
fn confirm_auto_impl_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
trait_def_id: DefId,
) -> ImplSourceAutoImplData<PredicateObligation<'tcx>> {
debug!("confirm_auto_impl_candidate({:?}, {:?})", obligation, trait_def_id);
let types = obligation.predicate.map_bound(|inner| {
let self_ty = self.infcx.shallow_resolve(inner.self_ty());
self.constituent_types_for_ty(self_ty)
});
self.vtable_auto_impl(obligation, trait_def_id, types)
}
/// See `confirm_auto_impl_candidate`.
fn vtable_auto_impl(
&mut self,
obligation: &TraitObligation<'tcx>,
trait_def_id: DefId,
nested: ty::Binder<Vec<Ty<'tcx>>>,
) -> ImplSourceAutoImplData<PredicateObligation<'tcx>> {
debug!("vtable_auto_impl: nested={:?}", nested);
ensure_sufficient_stack(|| {
let cause = obligation.derived_cause(BuiltinDerivedObligation);
let mut obligations = self.collect_predicates_for_types(
obligation.param_env,
cause,
obligation.recursion_depth + 1,
trait_def_id,
nested,
);
let trait_obligations: Vec<PredicateObligation<'_>> =
self.infcx.commit_unconditionally(|_| {
let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
let (trait_ref, _) =
self.infcx.replace_bound_vars_with_placeholders(&poly_trait_ref);
let cause = obligation.derived_cause(ImplDerivedObligation);
self.impl_or_trait_obligations(
cause,
obligation.recursion_depth + 1,
obligation.param_env,
trait_def_id,
&trait_ref.substs,
)
});
// Adds the predicates from the trait. Note that this contains a `Self: Trait`
// predicate as usual. It won't have any effect since auto traits are coinductive.
obligations.extend(trait_obligations);
debug!("vtable_auto_impl: obligations={:?}", obligations);
ImplSourceAutoImplData { trait_def_id, nested: obligations }
})
}
fn confirm_impl_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
impl_def_id: DefId,
) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> {
debug!("confirm_impl_candidate({:?},{:?})", obligation, impl_def_id);
// First, create the substitutions by matching the impl again,
// this time not in a probe.
self.infcx.commit_unconditionally(|_| {
let substs = self.rematch_impl(impl_def_id, obligation);
debug!("confirm_impl_candidate: substs={:?}", substs);
let cause = obligation.derived_cause(ImplDerivedObligation);
ensure_sufficient_stack(|| {
self.vtable_impl(
impl_def_id,
substs,
cause,
obligation.recursion_depth + 1,
obligation.param_env,
)
})
})
}
fn vtable_impl(
&mut self,
impl_def_id: DefId,
mut substs: Normalized<'tcx, SubstsRef<'tcx>>,
cause: ObligationCause<'tcx>,
recursion_depth: usize,
param_env: ty::ParamEnv<'tcx>,
) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> {
debug!(
"vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={})",
impl_def_id, substs, recursion_depth,
);
let mut impl_obligations = self.impl_or_trait_obligations(
cause,
recursion_depth,
param_env,
impl_def_id,
&substs.value,
);
debug!(
"vtable_impl: impl_def_id={:?} impl_obligations={:?}",
impl_def_id, impl_obligations
);
// Because of RFC447, the impl-trait-ref and obligations
// are sufficient to determine the impl substs, without
// relying on projections in the impl-trait-ref.
//
// e.g., `impl<U: Tr, V: Iterator<Item=U>> Foo<<U as Tr>::T> for V`
impl_obligations.append(&mut substs.obligations);
ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: impl_obligations }
}
fn confirm_object_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
) -> ImplSourceObjectData<'tcx, PredicateObligation<'tcx>> {
debug!("confirm_object_candidate({:?})", obligation);
// FIXME(nmatsakis) skipping binder here seems wrong -- we should
// probably flatten the binder from the obligation and the binder
// from the object. Have to try to make a broken test case that
// results.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
let poly_trait_ref = match self_ty.kind {
ty::Dynamic(ref data, ..) => data
.principal()
.unwrap_or_else(|| {
span_bug!(obligation.cause.span, "object candidate with no principal")
})
.with_self_ty(self.tcx(), self_ty),
_ => span_bug!(obligation.cause.span, "object candidate with non-object"),
};
let mut upcast_trait_ref = None;
let mut nested = vec![];
let vtable_base;
{
let tcx = self.tcx();
// We want to find the first supertrait in the list of
// supertraits that we can unify with, and do that
// unification. We know that there is exactly one in the list
// where we can unify, because otherwise select would have
// reported an ambiguity. (When we do find a match, also
// record it for later.)
let nonmatching = util::supertraits(tcx, poly_trait_ref).take_while(|&t| {
match self.infcx.commit_if_ok(|_| self.match_poly_trait_ref(obligation, t)) {
Ok(obligations) => {
upcast_trait_ref = Some(t);
nested.extend(obligations);
false
}
Err(_) => true,
}
});
// Additionally, for each of the non-matching predicates that
// we pass over, we sum up the set of number of vtable
// entries, so that we can compute the offset for the selected
// trait.
vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum();
}
ImplSourceObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested }
}
fn confirm_fn_pointer_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
) -> Result<ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
{
debug!("confirm_fn_pointer_candidate({:?})", obligation);
// Okay to skip binder; it is reintroduced below.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
let sig = self_ty.fn_sig(self.tcx());
let trait_ref = closure_trait_ref_and_return_type(
self.tcx(),
obligation.predicate.def_id(),
self_ty,
sig,
util::TupleArgumentsFlag::Yes,
)
.map_bound(|(trait_ref, _)| trait_ref);
let Normalized { value: trait_ref, obligations } = ensure_sufficient_stack(|| {
project::normalize_with_depth(
self,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
&trait_ref,
)
});
self.confirm_poly_trait_refs(
obligation.cause.clone(),
obligation.param_env,
obligation.predicate.to_poly_trait_ref(),
trait_ref,
)?;
Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested: obligations })
}
fn confirm_trait_alias_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
alias_def_id: DefId,
) -> ImplSourceTraitAliasData<'tcx, PredicateObligation<'tcx>> {
debug!("confirm_trait_alias_candidate({:?}, {:?})", obligation, alias_def_id);
self.infcx.commit_unconditionally(|_| {
let (predicate, _) =
self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate);
let trait_ref = predicate.trait_ref;
let trait_def_id = trait_ref.def_id;
let substs = trait_ref.substs;
let trait_obligations = self.impl_or_trait_obligations(
obligation.cause.clone(),
obligation.recursion_depth,
obligation.param_env,
trait_def_id,
&substs,
);
debug!(
"confirm_trait_alias_candidate: trait_def_id={:?} trait_obligations={:?}",
trait_def_id, trait_obligations
);
ImplSourceTraitAliasData { alias_def_id, substs, nested: trait_obligations }
})
}
fn confirm_generator_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
) -> Result<ImplSourceGeneratorData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
{
// Okay to skip binder because the substs on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
let (generator_def_id, substs) = match self_ty.kind {
ty::Generator(id, substs, _) => (id, substs),
_ => bug!("closure candidate for non-closure {:?}", obligation),
};
debug!("confirm_generator_candidate({:?},{:?},{:?})", obligation, generator_def_id, substs);
let trait_ref = self.generator_trait_ref_unnormalized(obligation, substs);
let Normalized { value: trait_ref, mut obligations } = ensure_sufficient_stack(|| {
normalize_with_depth(
self,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
&trait_ref,
)
});
debug!(
"confirm_generator_candidate(generator_def_id={:?}, \
trait_ref={:?}, obligations={:?})",
generator_def_id, trait_ref, obligations
);
obligations.extend(self.confirm_poly_trait_refs(
obligation.cause.clone(),
obligation.param_env,
obligation.predicate.to_poly_trait_ref(),
trait_ref,
)?);
Ok(ImplSourceGeneratorData { generator_def_id, substs, nested: obligations })
}
fn confirm_closure_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
) -> Result<ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
debug!("confirm_closure_candidate({:?})", obligation);
let kind = self
.tcx()
.fn_trait_kind_from_lang_item(obligation.predicate.def_id())
.unwrap_or_else(|| bug!("closure candidate for non-fn trait {:?}", obligation));
// Okay to skip binder because the substs on closure types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
let (closure_def_id, substs) = match self_ty.kind {
ty::Closure(id, substs) => (id, substs),
_ => bug!("closure candidate for non-closure {:?}", obligation),
};
let trait_ref = self.closure_trait_ref_unnormalized(obligation, substs);
let Normalized { value: trait_ref, mut obligations } = ensure_sufficient_stack(|| {
normalize_with_depth(
self,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
&trait_ref,
)
});
debug!(
"confirm_closure_candidate(closure_def_id={:?}, trait_ref={:?}, obligations={:?})",
closure_def_id, trait_ref, obligations
);
obligations.extend(self.confirm_poly_trait_refs(
obligation.cause.clone(),
obligation.param_env,
obligation.predicate.to_poly_trait_ref(),
trait_ref,
)?);
// FIXME: Chalk
if !self.tcx().sess.opts.debugging_opts.chalk {
obligations.push(Obligation::new(
obligation.cause.clone(),
obligation.param_env,
ty::PredicateAtom::ClosureKind(closure_def_id, substs, kind)
.to_predicate(self.tcx()),
));
}
Ok(ImplSourceClosureData { closure_def_id, substs, nested: obligations })
}
/// In the case of closure types and fn pointers,
/// we currently treat the input type parameters on the trait as
/// outputs. This means that when we have a match we have only
/// considered the self type, so we have to go back and make sure
/// to relate the argument types too. This is kind of wrong, but
/// since we control the full set of impls, also not that wrong,
/// and it DOES yield better error messages (since we don't report
/// errors as if there is no applicable impl, but rather report
/// errors are about mismatched argument types.
///
/// Here is an example. Imagine we have a closure expression
/// and we desugared it so that the type of the expression is
/// `Closure`, and `Closure` expects `i32` as argument. Then it
/// is "as if" the compiler generated this impl:
///
/// impl Fn(i32) for Closure { ... }
///
/// Now imagine our obligation is `Closure: Fn(usize)`. So far
/// we have matched the self type `Closure`. At this point we'll
/// compare the `i32` to `usize` and generate an error.
///
/// Note that this checking occurs *after* the impl has selected,
/// because these output type parameters should not affect the
/// selection of the impl. Therefore, if there is a mismatch, we
/// report an error to the user.
fn confirm_poly_trait_refs(
&mut self,
obligation_cause: ObligationCause<'tcx>,
obligation_param_env: ty::ParamEnv<'tcx>,
obligation_trait_ref: ty::PolyTraitRef<'tcx>,
expected_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
self.infcx
.at(&obligation_cause, obligation_param_env)
.sup(obligation_trait_ref, expected_trait_ref)
.map(|InferOk { obligations, .. }| obligations)
.map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e))
}
fn confirm_builtin_unsize_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
// `assemble_candidates_for_unsizing` should ensure there are no late-bound
// regions here. See the comment there for more details.
let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap());
let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1);
let target = self.infcx.shallow_resolve(target);
debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})", source, target);
let mut nested = vec![];
match (&source.kind, &target.kind) {
// Trait+Kx+'a -> Trait+Ky+'b (upcasts).
(&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => {
// See `assemble_candidates_for_unsizing` for more info.
let existential_predicates = data_a.map_bound(|data_a| {
let iter = data_a
.principal()
.map(ty::ExistentialPredicate::Trait)
.into_iter()
.chain(data_a.projection_bounds().map(ty::ExistentialPredicate::Projection))
.chain(data_b.auto_traits().map(ty::ExistentialPredicate::AutoTrait));
tcx.mk_existential_predicates(iter)
});
let source_trait = tcx.mk_dynamic(existential_predicates, r_b);
// Require that the traits involved in this upcast are **equal**;
// only the **lifetime bound** is changed.
let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
.sup(target, source_trait)
.map_err(|_| Unimplemented)?;
nested.extend(obligations);
// Register one obligation for 'a: 'b.
let cause = ObligationCause::new(
obligation.cause.span,
obligation.cause.body_id,
ObjectCastObligation(target),
);
let outlives = ty::OutlivesPredicate(r_a, r_b);
nested.push(Obligation::with_depth(
cause,
obligation.recursion_depth + 1,
obligation.param_env,
ty::Binder::bind(outlives).to_predicate(tcx),
));
}
// `T` -> `Trait`
(_, &ty::Dynamic(ref data, r)) => {
let mut object_dids = data.auto_traits().chain(data.principal_def_id());
if let Some(did) = object_dids.find(|did| !tcx.is_object_safe(*did)) {
return Err(TraitNotObjectSafe(did));
}
let cause = ObligationCause::new(
obligation.cause.span,
obligation.cause.body_id,
ObjectCastObligation(target),
);
let predicate_to_obligation = |predicate| {
Obligation::with_depth(
cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
predicate,
)
};
// Create obligations:
// - Casting `T` to `Trait`
// - For all the various builtin bounds attached to the object cast. (In other
// words, if the object type is `Foo + Send`, this would create an obligation for
// the `Send` check.)
// - Projection predicates
nested.extend(
data.iter().map(|predicate| {
predicate_to_obligation(predicate.with_self_ty(tcx, source))
}),
);
// We can only make objects from sized types.
let tr = ty::TraitRef::new(
tcx.require_lang_item(LangItem::Sized, None),
tcx.mk_substs_trait(source, &[]),
);
nested.push(predicate_to_obligation(tr.without_const().to_predicate(tcx)));
// If the type is `Foo + 'a`, ensure that the type
// being cast to `Foo + 'a` outlives `'a`:
let outlives = ty::OutlivesPredicate(source, r);
nested.push(predicate_to_obligation(ty::Binder::dummy(outlives).to_predicate(tcx)));
}
// `[T; n]` -> `[T]`
(&ty::Array(a, _), &ty::Slice(b)) => {
let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
.eq(b, a)
.map_err(|_| Unimplemented)?;
nested.extend(obligations);
}
// `Struct<T>` -> `Struct<U>`
(&ty::Adt(def, substs_a), &ty::Adt(_, substs_b)) => {
let maybe_unsizing_param_idx = |arg: GenericArg<'tcx>| match arg.unpack() {
GenericArgKind::Type(ty) => match ty.kind {
ty::Param(p) => Some(p.index),
_ => None,
},
// Lifetimes aren't allowed to change during unsizing.
GenericArgKind::Lifetime(_) => None,
GenericArgKind::Const(ct) => match ct.val {
ty::ConstKind::Param(p) => Some(p.index),
_ => None,
},
};
// The last field of the structure has to exist and contain type/const parameters.
let (tail_field, prefix_fields) =
def.non_enum_variant().fields.split_last().ok_or(Unimplemented)?;
let tail_field_ty = tcx.type_of(tail_field.did);
let mut unsizing_params = GrowableBitSet::new_empty();
let mut found = false;
for arg in tail_field_ty.walk() {
if let Some(i) = maybe_unsizing_param_idx(arg) {
unsizing_params.insert(i);
found = true;
}
}
if !found {
return Err(Unimplemented);
}
// Ensure none of the other fields mention the parameters used
// in unsizing.
// FIXME(eddyb) cache this (including computing `unsizing_params`)
// by putting it in a query; it would only need the `DefId` as it
// looks at declared field types, not anything substituted.
for field in prefix_fields {
for arg in tcx.type_of(field.did).walk() {
if let Some(i) = maybe_unsizing_param_idx(arg) {
if unsizing_params.contains(i) {
return Err(Unimplemented);
}
}
}
}
// Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`.
let source_tail = tail_field_ty.subst(tcx, substs_a);
let target_tail = tail_field_ty.subst(tcx, substs_b);
// Check that the source struct with the target's
// unsizing parameters is equal to the target.
let substs = tcx.mk_substs(substs_a.iter().enumerate().map(|(i, k)| {
if unsizing_params.contains(i as u32) { substs_b[i] } else { k }
}));
let new_struct = tcx.mk_adt(def, substs);
let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
.eq(target, new_struct)
.map_err(|_| Unimplemented)?;
nested.extend(obligations);
// Construct the nested `TailField<T>: Unsize<TailField<U>>` predicate.
nested.push(predicate_for_trait_def(
tcx,
obligation.param_env,
obligation.cause.clone(),
obligation.predicate.def_id(),
obligation.recursion_depth + 1,
source_tail,
&[target_tail.into()],
));
}
// `(.., T)` -> `(.., U)`
(&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => {
assert_eq!(tys_a.len(), tys_b.len());
// The last field of the tuple has to exist.
let (&a_last, a_mid) = tys_a.split_last().ok_or(Unimplemented)?;
let &b_last = tys_b.last().unwrap();
// Check that the source tuple with the target's
// last element is equal to the target.
let new_tuple = tcx.mk_tup(
a_mid.iter().map(|k| k.expect_ty()).chain(iter::once(b_last.expect_ty())),
);
let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
.eq(target, new_tuple)
.map_err(|_| Unimplemented)?;
nested.extend(obligations);
// Construct the nested `T: Unsize<U>` predicate.
nested.push(ensure_sufficient_stack(|| {
predicate_for_trait_def(
tcx,
obligation.param_env,
obligation.cause.clone(),
obligation.predicate.def_id(),
obligation.recursion_depth + 1,
a_last.expect_ty(),
&[b_last],
)
}));
}
_ => bug!(),
};
Ok(ImplSourceBuiltinData { nested })
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,518 @@
//! Logic and data structures related to impl specialization, explained in
//! greater detail below.
//!
//! At the moment, this implementation support only the simple "chain" rule:
//! If any two impls overlap, one must be a strict subset of the other.
//!
//! See the [rustc dev guide] for a bit more detail on how specialization
//! fits together with the rest of the trait machinery.
//!
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
pub mod specialization_graph;
use specialization_graph::GraphExt;
use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt};
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::struct_span_err;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::lint::LintDiagnosticBuilder;
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
use rustc_span::DUMMY_SP;
use super::util::impl_trait_ref_and_oblig;
use super::{FulfillmentContext, SelectionContext};
/// Information pertinent to an overlapping impl error.
#[derive(Debug)]
pub struct OverlapError {
pub with_impl: DefId,
pub trait_desc: String,
pub self_desc: Option<String>,
pub intercrate_ambiguity_causes: Vec<IntercrateAmbiguityCause>,
pub involves_placeholder: bool,
}
/// Given a subst for the requested impl, translate it to a subst
/// appropriate for the actual item definition (whether it be in that impl,
/// a parent impl, or the trait).
///
/// When we have selected one impl, but are actually using item definitions from
/// a parent impl providing a default, we need a way to translate between the
/// type parameters of the two impls. Here the `source_impl` is the one we've
/// selected, and `source_substs` is a substitution of its generics.
/// And `target_node` is the impl/trait we're actually going to get the
/// definition from. The resulting substitution will map from `target_node`'s
/// generics to `source_impl`'s generics as instantiated by `source_subst`.
///
/// For example, consider the following scenario:
///
/// ```rust
/// trait Foo { ... }
/// impl<T, U> Foo for (T, U) { ... } // target impl
/// impl<V> Foo for (V, V) { ... } // source impl
/// ```
///
/// Suppose we have selected "source impl" with `V` instantiated with `u32`.
/// This function will produce a substitution with `T` and `U` both mapping to `u32`.
///
/// where-clauses add some trickiness here, because they can be used to "define"
/// an argument indirectly:
///
/// ```rust
/// impl<'a, I, T: 'a> Iterator for Cloned<I>
/// where I: Iterator<Item = &'a T>, T: Clone
/// ```
///
/// In a case like this, the substitution for `T` is determined indirectly,
/// through associated type projection. We deal with such cases by using
/// *fulfillment* to relate the two impls, requiring that all projections are
/// resolved.
pub fn translate_substs<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
source_impl: DefId,
source_substs: SubstsRef<'tcx>,
target_node: specialization_graph::Node,
) -> SubstsRef<'tcx> {
debug!(
"translate_substs({:?}, {:?}, {:?}, {:?})",
param_env, source_impl, source_substs, target_node
);
let source_trait_ref =
infcx.tcx.impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs);
// translate the Self and Param parts of the substitution, since those
// vary across impls
let target_substs = match target_node {
specialization_graph::Node::Impl(target_impl) => {
// no need to translate if we're targeting the impl we started with
if source_impl == target_impl {
return source_substs;
}
fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else(
|_| {
bug!(
"When translating substitutions for specialization, the expected \
specialization failed to hold"
)
},
)
}
specialization_graph::Node::Trait(..) => source_trait_ref.substs,
};
// directly inherent the method generics, since those do not vary across impls
source_substs.rebase_onto(infcx.tcx, source_impl, target_substs)
}
/// Is `impl1` a specialization of `impl2`?
///
/// Specialization is determined by the sets of types to which the impls apply;
/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies
/// to.
pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool {
debug!("specializes({:?}, {:?})", impl1_def_id, impl2_def_id);
// The feature gate should prevent introducing new specializations, but not
// taking advantage of upstream ones.
let features = tcx.features();
let specialization_enabled = features.specialization || features.min_specialization;
if !specialization_enabled && (impl1_def_id.is_local() || impl2_def_id.is_local()) {
return false;
}
// We determine whether there's a subset relationship by:
//
// - replacing bound vars with placeholders in impl1,
// - assuming the where clauses for impl1,
// - instantiating impl2 with fresh inference variables,
// - unifying,
// - attempting to prove the where clauses for impl2
//
// The last three steps are encapsulated in `fulfill_implication`.
//
// See RFC 1210 for more details and justification.
// Currently we do not allow e.g., a negative impl to specialize a positive one
if tcx.impl_polarity(impl1_def_id) != tcx.impl_polarity(impl2_def_id) {
return false;
}
// create a parameter environment corresponding to a (placeholder) instantiation of impl1
let penv = tcx.param_env(impl1_def_id);
let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();
// Create a infcx, taking the predicates of impl1 as assumptions:
tcx.infer_ctxt().enter(|infcx| {
// Normalize the trait reference. The WF rules ought to ensure
// that this always succeeds.
let impl1_trait_ref = match traits::fully_normalize(
&infcx,
FulfillmentContext::new(),
ObligationCause::dummy(),
penv,
&impl1_trait_ref,
) {
Ok(impl1_trait_ref) => impl1_trait_ref,
Err(err) => {
bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err);
}
};
// Attempt to prove that impl2 applies, given all of the above.
fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok()
})
}
/// Attempt to fulfill all obligations of `target_impl` after unification with
/// `source_trait_ref`. If successful, returns a substitution for *all* the
/// generics of `target_impl`, including both those needed to unify with
/// `source_trait_ref` and those whose identity is determined via a where
/// clause in the impl.
fn fulfill_implication<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
source_trait_ref: ty::TraitRef<'tcx>,
target_impl: DefId,
) -> Result<SubstsRef<'tcx>, ()> {
debug!(
"fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
param_env, source_trait_ref, target_impl
);
let selcx = &mut SelectionContext::new(&infcx);
let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl);
let (target_trait_ref, obligations) =
impl_trait_ref_and_oblig(selcx, param_env, target_impl, target_substs);
// do the impls unify? If not, no specialization.
let more_obligations =
match infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref)
{
Ok(InferOk { obligations, .. }) => obligations,
Err(_) => {
debug!(
"fulfill_implication: {:?} does not unify with {:?}",
source_trait_ref, target_trait_ref
);
return Err(());
}
};
// attempt to prove all of the predicates for impl2 given those for impl1
// (which are packed up in penv)
infcx.save_and_restore_in_snapshot_flag(|infcx| {
// If we came from `translate_substs`, we already know that the
// predicates for our impl hold (after all, we know that a more
// specialized impl holds, so our impl must hold too), and
// we only want to process the projections to determine the
// the types in our substs using RFC 447, so we can safely
// ignore region obligations, which allows us to avoid threading
// a node-id to assign them with.
//
// If we came from specialization graph construction, then
// we already make a mockery out of the region system, so
// why not ignore them a bit earlier?
let mut fulfill_cx = FulfillmentContext::new_ignoring_regions();
for oblig in obligations.chain(more_obligations) {
fulfill_cx.register_predicate_obligation(&infcx, oblig);
}
match fulfill_cx.select_all_or_error(infcx) {
Err(errors) => {
// no dice!
debug!(
"fulfill_implication: for impls on {:?} and {:?}, \
could not fulfill: {:?} given {:?}",
source_trait_ref,
target_trait_ref,
errors,
param_env.caller_bounds()
);
Err(())
}
Ok(()) => {
debug!(
"fulfill_implication: an impl for {:?} specializes {:?}",
source_trait_ref, target_trait_ref
);
// Now resolve the *substitution* we built for the target earlier, replacing
// the inference variables inside with whatever we got from fulfillment.
Ok(infcx.resolve_vars_if_possible(&target_substs))
}
}
})
}
// Query provider for `specialization_graph_of`.
pub(super) fn specialization_graph_provider(
tcx: TyCtxt<'_>,
trait_id: DefId,
) -> specialization_graph::Graph {
let mut sg = specialization_graph::Graph::new();
let mut trait_impls: Vec<_> = tcx.all_impls(trait_id).collect();
// The coherence checking implementation seems to rely on impls being
// iterated over (roughly) in definition order, so we are sorting by
// negated `CrateNum` (so remote definitions are visited first) and then
// by a flattened version of the `DefIndex`.
trait_impls
.sort_unstable_by_key(|def_id| (-(def_id.krate.as_u32() as i64), def_id.index.index()));
for impl_def_id in trait_impls {
if let Some(impl_def_id) = impl_def_id.as_local() {
// This is where impl overlap checking happens:
let insert_result = sg.insert(tcx, impl_def_id.to_def_id());
// Report error if there was one.
let (overlap, used_to_be_allowed) = match insert_result {
Err(overlap) => (Some(overlap), None),
Ok(Some(overlap)) => (Some(overlap.error), Some(overlap.kind)),
Ok(None) => (None, None),
};
if let Some(overlap) = overlap {
report_overlap_conflict(tcx, overlap, impl_def_id, used_to_be_allowed, &mut sg);
}
} else {
let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id);
sg.record_impl_from_cstore(tcx, parent, impl_def_id)
}
}
sg
}
fn report_overlap_conflict(
tcx: TyCtxt<'_>,
overlap: OverlapError,
impl_def_id: LocalDefId,
used_to_be_allowed: Option<FutureCompatOverlapErrorKind>,
sg: &mut specialization_graph::Graph,
) {
let impl_polarity = tcx.impl_polarity(impl_def_id.to_def_id());
let other_polarity = tcx.impl_polarity(overlap.with_impl);
match (impl_polarity, other_polarity) {
(ty::ImplPolarity::Negative, ty::ImplPolarity::Positive) => {
report_negative_positive_conflict(
tcx,
&overlap,
impl_def_id,
impl_def_id.to_def_id(),
overlap.with_impl,
sg,
);
}
(ty::ImplPolarity::Positive, ty::ImplPolarity::Negative) => {
report_negative_positive_conflict(
tcx,
&overlap,
impl_def_id,
overlap.with_impl,
impl_def_id.to_def_id(),
sg,
);
}
_ => {
report_conflicting_impls(tcx, overlap, impl_def_id, used_to_be_allowed, sg);
}
}
}
fn report_negative_positive_conflict(
tcx: TyCtxt<'_>,
overlap: &OverlapError,
local_impl_def_id: LocalDefId,
negative_impl_def_id: DefId,
positive_impl_def_id: DefId,
sg: &mut specialization_graph::Graph,
) {
let impl_span = tcx
.sess
.source_map()
.guess_head_span(tcx.span_of_impl(local_impl_def_id.to_def_id()).unwrap());
let mut err = struct_span_err!(
tcx.sess,
impl_span,
E0751,
"found both positive and negative implementation of trait `{}`{}:",
overlap.trait_desc,
overlap.self_desc.clone().map_or(String::new(), |ty| format!(" for type `{}`", ty))
);
match tcx.span_of_impl(negative_impl_def_id) {
Ok(span) => {
err.span_label(
tcx.sess.source_map().guess_head_span(span),
"negative implementation here".to_string(),
);
}
Err(cname) => {
err.note(&format!("negative implementation in crate `{}`", cname));
}
}
match tcx.span_of_impl(positive_impl_def_id) {
Ok(span) => {
err.span_label(
tcx.sess.source_map().guess_head_span(span),
"positive implementation here".to_string(),
);
}
Err(cname) => {
err.note(&format!("positive implementation in crate `{}`", cname));
}
}
sg.has_errored = true;
err.emit();
}
fn report_conflicting_impls(
tcx: TyCtxt<'_>,
overlap: OverlapError,
impl_def_id: LocalDefId,
used_to_be_allowed: Option<FutureCompatOverlapErrorKind>,
sg: &mut specialization_graph::Graph,
) {
let impl_span =
tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id.to_def_id()).unwrap());
// Work to be done after we've built the DiagnosticBuilder. We have to define it
// now because the struct_lint methods don't return back the DiagnosticBuilder
// that's passed in.
let decorate = |err: LintDiagnosticBuilder<'_>| {
let msg = format!(
"conflicting implementations of trait `{}`{}:{}",
overlap.trait_desc,
overlap.self_desc.clone().map_or(String::new(), |ty| { format!(" for type `{}`", ty) }),
match used_to_be_allowed {
Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)",
_ => "",
}
);
let mut err = err.build(&msg);
match tcx.span_of_impl(overlap.with_impl) {
Ok(span) => {
err.span_label(
tcx.sess.source_map().guess_head_span(span),
"first implementation here".to_string(),
);
err.span_label(
impl_span,
format!(
"conflicting implementation{}",
overlap.self_desc.map_or(String::new(), |ty| format!(" for `{}`", ty))
),
);
}
Err(cname) => {
let msg = match to_pretty_impl_header(tcx, overlap.with_impl) {
Some(s) => format!("conflicting implementation in crate `{}`:\n- {}", cname, s),
None => format!("conflicting implementation in crate `{}`", cname),
};
err.note(&msg);
}
}
for cause in &overlap.intercrate_ambiguity_causes {
cause.add_intercrate_ambiguity_hint(&mut err);
}
if overlap.involves_placeholder {
coherence::add_placeholder_note(&mut err);
}
err.emit()
};
match used_to_be_allowed {
None => {
sg.has_errored = true;
let err = struct_span_err!(tcx.sess, impl_span, E0119, "");
decorate(LintDiagnosticBuilder::new(err));
}
Some(kind) => {
let lint = match kind {
FutureCompatOverlapErrorKind::Issue33140 => ORDER_DEPENDENT_TRAIT_OBJECTS,
FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK,
};
tcx.struct_span_lint_hir(
lint,
tcx.hir().local_def_id_to_hir_id(impl_def_id),
impl_span,
decorate,
)
}
};
}
/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a
/// string.
fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> {
use std::fmt::Write;
let trait_ref = tcx.impl_trait_ref(impl_def_id)?;
let mut w = "impl".to_owned();
let substs = InternalSubsts::identity_for_item(tcx, impl_def_id);
// FIXME: Currently only handles ?Sized.
// Needs to support ?Move and ?DynSized when they are implemented.
let mut types_without_default_bounds = FxHashSet::default();
let sized_trait = tcx.lang_items().sized_trait();
if !substs.is_noop() {
types_without_default_bounds.extend(substs.types());
w.push('<');
w.push_str(
&substs
.iter()
.map(|k| k.to_string())
.filter(|k| k != "'_")
.collect::<Vec<_>>()
.join(", "),
);
w.push('>');
}
write!(w, " {} for {}", trait_ref.print_only_trait_path(), tcx.type_of(impl_def_id)).unwrap();
// The predicates will contain default bounds like `T: Sized`. We need to
// remove these bounds, and add `T: ?Sized` to any untouched type parameters.
let predicates = tcx.predicates_of(impl_def_id).predicates;
let mut pretty_predicates =
Vec::with_capacity(predicates.len() + types_without_default_bounds.len());
for (p, _) in predicates {
if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() {
if Some(poly_trait_ref.def_id()) == sized_trait {
types_without_default_bounds.remove(poly_trait_ref.self_ty().skip_binder());
continue;
}
}
pretty_predicates.push(p.to_string());
}
pretty_predicates
.extend(types_without_default_bounds.iter().map(|ty| format!("{}: ?Sized", ty)));
if !pretty_predicates.is_empty() {
write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap();
}
w.push(';');
Some(w)
}

View file

@ -0,0 +1,379 @@
use super::OverlapError;
use crate::traits;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::fast_reject::{self, SimplifiedType};
use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
pub use rustc_middle::traits::specialization_graph::*;
#[derive(Copy, Clone, Debug)]
pub enum FutureCompatOverlapErrorKind {
Issue33140,
LeakCheck,
}
#[derive(Debug)]
pub struct FutureCompatOverlapError {
pub error: OverlapError,
pub kind: FutureCompatOverlapErrorKind,
}
/// The result of attempting to insert an impl into a group of children.
enum Inserted {
/// The impl was inserted as a new child in this group of children.
BecameNewSibling(Option<FutureCompatOverlapError>),
/// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc.
ReplaceChildren(Vec<DefId>),
/// The impl is a specialization of an existing child.
ShouldRecurseOn(DefId),
}
trait ChildrenExt {
fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId);
fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId);
fn insert(
&mut self,
tcx: TyCtxt<'tcx>,
impl_def_id: DefId,
simplified_self: Option<SimplifiedType>,
) -> Result<Inserted, OverlapError>;
}
impl ChildrenExt for Children {
/// Insert an impl into this set of children without comparing to any existing impls.
fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) {
debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st);
self.nonblanket_impls.entry(st).or_default().push(impl_def_id)
} else {
debug!("insert_blindly: impl_def_id={:?} st=None", impl_def_id);
self.blanket_impls.push(impl_def_id)
}
}
/// Removes an impl from this set of children. Used when replacing
/// an impl with a parent. The impl must be present in the list of
/// children already.
fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
let vec: &mut Vec<DefId>;
if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) {
debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st);
vec = self.nonblanket_impls.get_mut(&st).unwrap();
} else {
debug!("remove_existing: impl_def_id={:?} st=None", impl_def_id);
vec = &mut self.blanket_impls;
}
let index = vec.iter().position(|d| *d == impl_def_id).unwrap();
vec.remove(index);
}
/// Attempt to insert an impl into this set of children, while comparing for
/// specialization relationships.
fn insert(
&mut self,
tcx: TyCtxt<'tcx>,
impl_def_id: DefId,
simplified_self: Option<SimplifiedType>,
) -> Result<Inserted, OverlapError> {
let mut last_lint = None;
let mut replace_children = Vec::new();
debug!("insert(impl_def_id={:?}, simplified_self={:?})", impl_def_id, simplified_self,);
let possible_siblings = match simplified_self {
Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)),
None => PotentialSiblings::Unfiltered(iter_children(self)),
};
for possible_sibling in possible_siblings {
debug!(
"insert: impl_def_id={:?}, simplified_self={:?}, possible_sibling={:?}",
impl_def_id, simplified_self, possible_sibling,
);
let create_overlap_error = |overlap: traits::coherence::OverlapResult<'_>| {
let trait_ref = overlap.impl_header.trait_ref.unwrap();
let self_ty = trait_ref.self_ty();
OverlapError {
with_impl: possible_sibling,
trait_desc: trait_ref.print_only_trait_path().to_string(),
// Only report the `Self` type if it has at least
// some outer concrete shell; otherwise, it's
// not adding much information.
self_desc: if self_ty.has_concrete_skeleton() {
Some(self_ty.to_string())
} else {
None
},
intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
involves_placeholder: overlap.involves_placeholder,
}
};
let report_overlap_error = |overlap: traits::coherence::OverlapResult<'_>,
last_lint: &mut _| {
// Found overlap, but no specialization; error out or report future-compat warning.
// Do we *still* get overlap if we disable the future-incompatible modes?
let should_err = traits::overlapping_impls(
tcx,
possible_sibling,
impl_def_id,
traits::SkipLeakCheck::default(),
|_| true,
|| false,
);
let error = create_overlap_error(overlap);
if should_err {
Err(error)
} else {
*last_lint = Some(FutureCompatOverlapError {
error,
kind: FutureCompatOverlapErrorKind::LeakCheck,
});
Ok((false, false))
}
};
let last_lint_mut = &mut last_lint;
let (le, ge) = traits::overlapping_impls(
tcx,
possible_sibling,
impl_def_id,
traits::SkipLeakCheck::Yes,
|overlap| {
if let Some(overlap_kind) =
tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling)
{
match overlap_kind {
ty::ImplOverlapKind::Permitted { marker: _ } => {}
ty::ImplOverlapKind::Issue33140 => {
*last_lint_mut = Some(FutureCompatOverlapError {
error: create_overlap_error(overlap),
kind: FutureCompatOverlapErrorKind::Issue33140,
});
}
}
return Ok((false, false));
}
let le = tcx.specializes((impl_def_id, possible_sibling));
let ge = tcx.specializes((possible_sibling, impl_def_id));
if le == ge {
report_overlap_error(overlap, last_lint_mut)
} else {
Ok((le, ge))
}
},
|| Ok((false, false)),
)?;
if le && !ge {
debug!(
"descending as child of TraitRef {:?}",
tcx.impl_trait_ref(possible_sibling).unwrap()
);
// The impl specializes `possible_sibling`.
return Ok(Inserted::ShouldRecurseOn(possible_sibling));
} else if ge && !le {
debug!(
"placing as parent of TraitRef {:?}",
tcx.impl_trait_ref(possible_sibling).unwrap()
);
replace_children.push(possible_sibling);
} else {
// Either there's no overlap, or the overlap was already reported by
// `overlap_error`.
}
}
if !replace_children.is_empty() {
return Ok(Inserted::ReplaceChildren(replace_children));
}
// No overlap with any potential siblings, so add as a new sibling.
debug!("placing as new sibling");
self.insert_blindly(tcx, impl_def_id);
Ok(Inserted::BecameNewSibling(last_lint))
}
}
fn iter_children(children: &mut Children) -> impl Iterator<Item = DefId> + '_ {
let nonblanket = children.nonblanket_impls.iter_mut().flat_map(|(_, v)| v.iter());
children.blanket_impls.iter().chain(nonblanket).cloned()
}
fn filtered_children(
children: &mut Children,
st: SimplifiedType,
) -> impl Iterator<Item = DefId> + '_ {
let nonblanket = children.nonblanket_impls.entry(st).or_default().iter();
children.blanket_impls.iter().chain(nonblanket).cloned()
}
// A custom iterator used by Children::insert
enum PotentialSiblings<I, J>
where
I: Iterator<Item = DefId>,
J: Iterator<Item = DefId>,
{
Unfiltered(I),
Filtered(J),
}
impl<I, J> Iterator for PotentialSiblings<I, J>
where
I: Iterator<Item = DefId>,
J: Iterator<Item = DefId>,
{
type Item = DefId;
fn next(&mut self) -> Option<Self::Item> {
match *self {
PotentialSiblings::Unfiltered(ref mut iter) => iter.next(),
PotentialSiblings::Filtered(ref mut iter) => iter.next(),
}
}
}
pub trait GraphExt {
/// Insert a local impl into the specialization graph. If an existing impl
/// conflicts with it (has overlap, but neither specializes the other),
/// information about the area of overlap is returned in the `Err`.
fn insert(
&mut self,
tcx: TyCtxt<'tcx>,
impl_def_id: DefId,
) -> Result<Option<FutureCompatOverlapError>, OverlapError>;
/// Insert cached metadata mapping from a child impl back to its parent.
fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId);
}
impl GraphExt for Graph {
/// Insert a local impl into the specialization graph. If an existing impl
/// conflicts with it (has overlap, but neither specializes the other),
/// information about the area of overlap is returned in the `Err`.
fn insert(
&mut self,
tcx: TyCtxt<'tcx>,
impl_def_id: DefId,
) -> Result<Option<FutureCompatOverlapError>, OverlapError> {
assert!(impl_def_id.is_local());
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
let trait_def_id = trait_ref.def_id;
debug!(
"insert({:?}): inserting TraitRef {:?} into specialization graph",
impl_def_id, trait_ref
);
// If the reference itself contains an earlier error (e.g., due to a
// resolution failure), then we just insert the impl at the top level of
// the graph and claim that there's no overlap (in order to suppress
// bogus errors).
if trait_ref.references_error() {
debug!(
"insert: inserting dummy node for erroneous TraitRef {:?}, \
impl_def_id={:?}, trait_def_id={:?}",
trait_ref, impl_def_id, trait_def_id
);
self.parent.insert(impl_def_id, trait_def_id);
self.children.entry(trait_def_id).or_default().insert_blindly(tcx, impl_def_id);
return Ok(None);
}
let mut parent = trait_def_id;
let mut last_lint = None;
let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false);
// Descend the specialization tree, where `parent` is the current parent node.
loop {
use self::Inserted::*;
let insert_result =
self.children.entry(parent).or_default().insert(tcx, impl_def_id, simplified)?;
match insert_result {
BecameNewSibling(opt_lint) => {
last_lint = opt_lint;
break;
}
ReplaceChildren(grand_children_to_be) => {
// We currently have
//
// P
// |
// G
//
// and we are inserting the impl N. We want to make it:
//
// P
// |
// N
// |
// G
// Adjust P's list of children: remove G and then add N.
{
let siblings = self.children.get_mut(&parent).unwrap();
for &grand_child_to_be in &grand_children_to_be {
siblings.remove_existing(tcx, grand_child_to_be);
}
siblings.insert_blindly(tcx, impl_def_id);
}
// Set G's parent to N and N's parent to P.
for &grand_child_to_be in &grand_children_to_be {
self.parent.insert(grand_child_to_be, impl_def_id);
}
self.parent.insert(impl_def_id, parent);
// Add G as N's child.
for &grand_child_to_be in &grand_children_to_be {
self.children
.entry(impl_def_id)
.or_default()
.insert_blindly(tcx, grand_child_to_be);
}
break;
}
ShouldRecurseOn(new_parent) => {
parent = new_parent;
}
}
}
self.parent.insert(impl_def_id, parent);
Ok(last_lint)
}
/// Insert cached metadata mapping from a child impl back to its parent.
fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) {
if self.parent.insert(child, parent).is_some() {
bug!(
"When recording an impl from the crate store, information about its parent \
was already present."
);
}
self.children.entry(parent).or_default().insert_blindly(tcx, child);
}
}

View file

@ -0,0 +1,281 @@
use crate::infer::{InferCtxt, TyCtxtInferExt};
use crate::traits::ObligationCause;
use crate::traits::{self, TraitEngine};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeFoldable, TypeVisitor};
use rustc_span::Span;
#[derive(Debug)]
pub enum NonStructuralMatchTy<'tcx> {
Adt(&'tcx AdtDef),
Param,
Dynamic,
Foreign,
Opaque,
Generator,
Projection,
Closure,
}
/// This method traverses the structure of `ty`, trying to find an
/// instance of an ADT (i.e. struct or enum) that doesn't implement
/// the structural-match traits, or a generic type parameter
/// (which cannot be determined to be structural-match).
///
/// The "structure of a type" includes all components that would be
/// considered when doing a pattern match on a constant of that
/// type.
///
/// * This means this method descends into fields of structs/enums,
/// and also descends into the inner type `T` of `&T` and `&mut T`
///
/// * The traversal doesn't dereference unsafe pointers (`*const T`,
/// `*mut T`), and it does not visit the type arguments of an
/// instantiated generic like `PhantomData<T>`.
///
/// The reason we do this search is Rust currently require all ADTs
/// reachable from a constant's type to implement the
/// structural-match traits, which essentially say that
/// the implementation of `PartialEq::eq` behaves *equivalently* to a
/// comparison against the unfolded structure.
///
/// For more background on why Rust has this requirement, and issues
/// that arose when the requirement was not enforced completely, see
/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
pub fn search_for_structural_match_violation<'tcx>(
_id: hir::HirId,
span: Span,
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Option<NonStructuralMatchTy<'tcx>> {
// FIXME: we should instead pass in an `infcx` from the outside.
tcx.infer_ctxt().enter(|infcx| {
let mut search = Search { infcx, span, found: None, seen: FxHashSet::default() };
ty.visit_with(&mut search);
search.found
})
}
/// This method returns true if and only if `adt_ty` itself has been marked as
/// eligible for structural-match: namely, if it implements both
/// `StructuralPartialEq` and `StructuralEq` (which are respectively injected by
/// `#[derive(PartialEq)]` and `#[derive(Eq)]`).
///
/// Note that this does *not* recursively check if the substructure of `adt_ty`
/// implements the traits.
fn type_marked_structural(
infcx: &InferCtxt<'_, 'tcx>,
adt_ty: Ty<'tcx>,
cause: ObligationCause<'tcx>,
) -> bool {
let mut fulfillment_cx = traits::FulfillmentContext::new();
// require `#[derive(PartialEq)]`
let structural_peq_def_id =
infcx.tcx.require_lang_item(LangItem::StructuralPeq, Some(cause.span));
fulfillment_cx.register_bound(
infcx,
ty::ParamEnv::empty(),
adt_ty,
structural_peq_def_id,
cause.clone(),
);
// for now, require `#[derive(Eq)]`. (Doing so is a hack to work around
// the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.)
let structural_teq_def_id =
infcx.tcx.require_lang_item(LangItem::StructuralTeq, Some(cause.span));
fulfillment_cx.register_bound(
infcx,
ty::ParamEnv::empty(),
adt_ty,
structural_teq_def_id,
cause,
);
// We deliberately skip *reporting* fulfillment errors (via
// `report_fulfillment_errors`), for two reasons:
//
// 1. The error messages would mention `std::marker::StructuralPartialEq`
// (a trait which is solely meant as an implementation detail
// for now), and
//
// 2. We are sometimes doing future-incompatibility lints for
// now, so we do not want unconditional errors here.
fulfillment_cx.select_all_or_error(infcx).is_ok()
}
/// This implements the traversal over the structure of a given type to try to
/// find instances of ADTs (specifically structs or enums) that do not implement
/// the structural-match traits (`StructuralPartialEq` and `StructuralEq`).
struct Search<'a, 'tcx> {
span: Span,
infcx: InferCtxt<'a, 'tcx>,
/// Records first ADT that does not implement a structural-match trait.
found: Option<NonStructuralMatchTy<'tcx>>,
/// Tracks ADTs previously encountered during search, so that
/// we will not recur on them again.
seen: FxHashSet<hir::def_id::DefId>,
}
impl Search<'a, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool {
adt_ty.is_structural_eq_shallow(self.tcx())
}
}
impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> {
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
debug!("Search visiting ty: {:?}", ty);
let (adt_def, substs) = match ty.kind {
ty::Adt(adt_def, substs) => (adt_def, substs),
ty::Param(_) => {
self.found = Some(NonStructuralMatchTy::Param);
return true; // Stop visiting.
}
ty::Dynamic(..) => {
self.found = Some(NonStructuralMatchTy::Dynamic);
return true; // Stop visiting.
}
ty::Foreign(_) => {
self.found = Some(NonStructuralMatchTy::Foreign);
return true; // Stop visiting.
}
ty::Opaque(..) => {
self.found = Some(NonStructuralMatchTy::Opaque);
return true; // Stop visiting.
}
ty::Projection(..) => {
self.found = Some(NonStructuralMatchTy::Projection);
return true; // Stop visiting.
}
ty::Generator(..) | ty::GeneratorWitness(..) => {
self.found = Some(NonStructuralMatchTy::Generator);
return true; // Stop visiting.
}
ty::Closure(..) => {
self.found = Some(NonStructuralMatchTy::Closure);
return true; // Stop visiting.
}
ty::RawPtr(..) => {
// structural-match ignores substructure of
// `*const _`/`*mut _`, so skip `super_visit_with`.
//
// For example, if you have:
// ```
// struct NonStructural;
// #[derive(PartialEq, Eq)]
// struct T(*const NonStructural);
// const C: T = T(std::ptr::null());
// ```
//
// Even though `NonStructural` does not implement `PartialEq`,
// structural equality on `T` does not recur into the raw
// pointer. Therefore, one can still use `C` in a pattern.
// (But still tell the caller to continue search.)
return false;
}
ty::FnDef(..) | ty::FnPtr(..) => {
// Types of formals and return in `fn(_) -> _` are also irrelevant;
// so we do not recur into them via `super_visit_with`
//
// (But still tell the caller to continue search.)
return false;
}
ty::Array(_, n)
if { n.try_eval_usize(self.tcx(), ty::ParamEnv::reveal_all()) == Some(0) } =>
{
// rust-lang/rust#62336: ignore type of contents
// for empty array.
//
// (But still tell the caller to continue search.)
return false;
}
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => {
// These primitive types are always structural match.
//
// `Never` is kind of special here, but as it is not inhabitable, this should be fine.
//
// (But still tell the caller to continue search.)
return false;
}
ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => {
// First check all contained types and then tell the caller to continue searching.
ty.super_visit_with(self);
return false;
}
ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => {
bug!("unexpected type during structural-match checking: {:?}", ty);
}
ty::Error(_) => {
self.tcx().sess.delay_span_bug(self.span, "ty::Error in structural-match check");
// We still want to check other types after encountering an error,
// as this may still emit relevant errors.
//
// So we continue searching here.
return false;
}
};
if !self.seen.insert(adt_def.did) {
debug!("Search already seen adt_def: {:?}", adt_def);
// Let caller continue its search.
return false;
}
if !self.type_marked_structural(ty) {
debug!("Search found ty: {:?}", ty);
self.found = Some(NonStructuralMatchTy::Adt(&adt_def));
return true; // Halt visiting!
}
// structural-match does not care about the
// instantiation of the generics in an ADT (it
// instead looks directly at its fields outside
// this match), so we skip super_visit_with.
//
// (Must not recur on substs for `PhantomData<T>` cf
// rust-lang/rust#55028 and rust-lang/rust#55837; but also
// want to skip substs when only uses of generic are
// behind unsafe pointers `*const T`/`*mut T`.)
// even though we skip super_visit_with, we must recur on
// fields of ADT.
let tcx = self.tcx();
for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) {
let ty = self.tcx().normalize_erasing_regions(ty::ParamEnv::empty(), field_ty);
debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty);
if ty.visit_with(self) {
// found an ADT without structural-match; halt visiting!
assert!(self.found.is_some());
return true;
}
}
// Even though we do not want to recur on substs, we do
// want our caller to continue its own search.
false
}
}
pub fn provide(providers: &mut Providers) {
providers.has_structural_eq_impls = |tcx, ty| {
tcx.infer_ctxt().enter(|infcx| {
let cause = ObligationCause::dummy();
type_marked_structural(&infcx, ty, cause)
})
};
}

View file

@ -0,0 +1,363 @@
use rustc_errors::DiagnosticBuilder;
use rustc_span::Span;
use smallvec::smallvec;
use smallvec::SmallVec;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness};
use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext};
pub use rustc_infer::traits::util::*;
///////////////////////////////////////////////////////////////////////////
// `TraitAliasExpander` iterator
///////////////////////////////////////////////////////////////////////////
/// "Trait alias expansion" is the process of expanding a sequence of trait
/// references into another sequence by transitively following all trait
/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias
/// `trait Foo = Bar + Sync;`, and another trait alias
/// `trait Bar = Read + Write`, then the bounds would expand to
/// `Read + Write + Sync + Send`.
/// Expansion is done via a DFS (depth-first search), and the `visited` field
/// is used to avoid cycles.
pub struct TraitAliasExpander<'tcx> {
tcx: TyCtxt<'tcx>,
stack: Vec<TraitAliasExpansionInfo<'tcx>>,
}
/// Stores information about the expansion of a trait via a path of zero or more trait aliases.
#[derive(Debug, Clone)]
pub struct TraitAliasExpansionInfo<'tcx> {
pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>,
}
impl<'tcx> TraitAliasExpansionInfo<'tcx> {
fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self {
Self { path: smallvec![(trait_ref, span)] }
}
/// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate
/// trait aliases.
pub fn label_with_exp_info(
&self,
diag: &mut DiagnosticBuilder<'_>,
top_label: &str,
use_desc: &str,
) {
diag.span_label(self.top().1, top_label);
if self.path.len() > 1 {
for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) {
diag.span_label(*sp, format!("referenced here ({})", use_desc));
}
}
if self.top().1 != self.bottom().1 {
// When the trait object is in a return type these two spans match, we don't want
// redundant labels.
diag.span_label(
self.bottom().1,
format!("trait alias used in trait object type ({})", use_desc),
);
}
}
pub fn trait_ref(&self) -> ty::PolyTraitRef<'tcx> {
self.top().0
}
pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) {
self.path.last().unwrap()
}
pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) {
self.path.first().unwrap()
}
fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self {
let mut path = self.path.clone();
path.push((trait_ref, span));
Self { path }
}
}
pub fn expand_trait_aliases<'tcx>(
tcx: TyCtxt<'tcx>,
trait_refs: impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)>,
) -> TraitAliasExpander<'tcx> {
let items: Vec<_> =
trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect();
TraitAliasExpander { tcx, stack: items }
}
impl<'tcx> TraitAliasExpander<'tcx> {
/// If `item` is a trait alias and its predicate has not yet been visited, then expands `item`
/// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`.
/// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a
/// trait alias.
/// The return value indicates whether `item` should be yielded to the user.
fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool {
let tcx = self.tcx;
let trait_ref = item.trait_ref();
let pred = trait_ref.without_const().to_predicate(tcx);
debug!("expand_trait_aliases: trait_ref={:?}", trait_ref);
// Don't recurse if this bound is not a trait alias.
let is_alias = tcx.is_trait_alias(trait_ref.def_id());
if !is_alias {
return true;
}
// Don't recurse if this trait alias is already on the stack for the DFS search.
let anon_pred = anonymize_predicate(tcx, pred);
if item.path.iter().rev().skip(1).any(|&(tr, _)| {
anonymize_predicate(tcx, tr.without_const().to_predicate(tcx)) == anon_pred
}) {
return false;
}
// Get components of trait alias.
let predicates = tcx.super_predicates_of(trait_ref.def_id());
let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
pred.subst_supertrait(tcx, &trait_ref)
.to_opt_poly_trait_ref()
.map(|trait_ref| item.clone_and_push(trait_ref, *span))
});
debug!("expand_trait_aliases: items={:?}", items.clone());
self.stack.extend(items);
false
}
}
impl<'tcx> Iterator for TraitAliasExpander<'tcx> {
type Item = TraitAliasExpansionInfo<'tcx>;
fn size_hint(&self) -> (usize, Option<usize>) {
(self.stack.len(), None)
}
fn next(&mut self) -> Option<TraitAliasExpansionInfo<'tcx>> {
while let Some(item) = self.stack.pop() {
if self.expand(&item) {
return Some(item);
}
}
None
}
}
///////////////////////////////////////////////////////////////////////////
// Iterator over def-IDs of supertraits
///////////////////////////////////////////////////////////////////////////
pub struct SupertraitDefIds<'tcx> {
tcx: TyCtxt<'tcx>,
stack: Vec<DefId>,
visited: FxHashSet<DefId>,
}
pub fn supertrait_def_ids(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SupertraitDefIds<'_> {
SupertraitDefIds {
tcx,
stack: vec![trait_def_id],
visited: Some(trait_def_id).into_iter().collect(),
}
}
impl Iterator for SupertraitDefIds<'tcx> {
type Item = DefId;
fn next(&mut self) -> Option<DefId> {
let def_id = self.stack.pop()?;
let predicates = self.tcx.super_predicates_of(def_id);
let visited = &mut self.visited;
self.stack.extend(
predicates
.predicates
.iter()
.filter_map(|(pred, _)| pred.to_opt_poly_trait_ref())
.map(|trait_ref| trait_ref.def_id())
.filter(|&super_def_id| visited.insert(super_def_id)),
);
Some(def_id)
}
}
///////////////////////////////////////////////////////////////////////////
// Other
///////////////////////////////////////////////////////////////////////////
/// Instantiate all bound parameters of the impl with the given substs,
/// returning the resulting trait ref and all obligations that arise.
/// The obligations are closed under normalization.
pub fn impl_trait_ref_and_oblig<'a, 'tcx>(
selcx: &mut SelectionContext<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
impl_def_id: DefId,
impl_substs: SubstsRef<'tcx>,
) -> (ty::TraitRef<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap();
let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs);
let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } =
super::normalize(selcx, param_env, ObligationCause::dummy(), &impl_trait_ref);
let predicates = selcx.tcx().predicates_of(impl_def_id);
let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
let Normalized { value: predicates, obligations: normalization_obligations2 } =
super::normalize(selcx, param_env, ObligationCause::dummy(), &predicates);
let impl_obligations =
predicates_for_generics(ObligationCause::dummy(), 0, param_env, predicates);
let impl_obligations = impl_obligations
.chain(normalization_obligations1.into_iter())
.chain(normalization_obligations2.into_iter());
(impl_trait_ref, impl_obligations)
}
pub fn predicates_for_generics<'tcx>(
cause: ObligationCause<'tcx>,
recursion_depth: usize,
param_env: ty::ParamEnv<'tcx>,
generic_bounds: ty::InstantiatedPredicates<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds);
generic_bounds.predicates.into_iter().map(move |predicate| Obligation {
cause: cause.clone(),
recursion_depth,
param_env,
predicate,
})
}
pub fn predicate_for_trait_ref<'tcx>(
tcx: TyCtxt<'tcx>,
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
recursion_depth: usize,
) -> PredicateObligation<'tcx> {
Obligation {
cause,
param_env,
recursion_depth,
predicate: trait_ref.without_const().to_predicate(tcx),
}
}
pub fn predicate_for_trait_def(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
trait_def_id: DefId,
recursion_depth: usize,
self_ty: Ty<'tcx>,
params: &[GenericArg<'tcx>],
) -> PredicateObligation<'tcx> {
let trait_ref =
ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(self_ty, params) };
predicate_for_trait_ref(tcx, cause, param_env, trait_ref, recursion_depth)
}
/// Casts a trait reference into a reference to one of its super
/// traits; returns `None` if `target_trait_def_id` is not a
/// supertrait.
pub fn upcast_choices(
tcx: TyCtxt<'tcx>,
source_trait_ref: ty::PolyTraitRef<'tcx>,
target_trait_def_id: DefId,
) -> Vec<ty::PolyTraitRef<'tcx>> {
if source_trait_ref.def_id() == target_trait_def_id {
return vec![source_trait_ref]; // Shortcut the most common case.
}
supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect()
}
/// Given a trait `trait_ref`, returns the number of vtable entries
/// that come from `trait_ref`, excluding its supertraits. Used in
/// computing the vtable base for an upcast trait of a trait object.
pub fn count_own_vtable_entries(tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> usize {
let mut entries = 0;
// Count number of methods and add them to the total offset.
// Skip over associated types and constants.
for trait_item in tcx.associated_items(trait_ref.def_id()).in_definition_order() {
if trait_item.kind == ty::AssocKind::Fn {
entries += 1;
}
}
entries
}
/// Given an upcast trait object described by `object`, returns the
/// index of the method `method_def_id` (which should be part of
/// `object.upcast_trait_ref`) within the vtable for `object`.
pub fn get_vtable_index_of_object_method<N>(
tcx: TyCtxt<'tcx>,
object: &super::ImplSourceObjectData<'tcx, N>,
method_def_id: DefId,
) -> usize {
// Count number of methods preceding the one we are selecting and
// add them to the total offset.
// Skip over associated types and constants, as those aren't stored in the vtable.
let mut entries = object.vtable_base;
for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()).in_definition_order() {
if trait_item.def_id == method_def_id {
// The item with the ID we were given really ought to be a method.
assert_eq!(trait_item.kind, ty::AssocKind::Fn);
return entries;
}
if trait_item.kind == ty::AssocKind::Fn {
entries += 1;
}
}
bug!("get_vtable_index_of_object_method: {:?} was not found", method_def_id);
}
pub fn closure_trait_ref_and_return_type(
tcx: TyCtxt<'tcx>,
fn_trait_def_id: DefId,
self_ty: Ty<'tcx>,
sig: ty::PolyFnSig<'tcx>,
tuple_arguments: TupleArgumentsFlag,
) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)> {
let arguments_tuple = match tuple_arguments {
TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
TupleArgumentsFlag::Yes => tcx.intern_tup(sig.skip_binder().inputs()),
};
let trait_ref = ty::TraitRef {
def_id: fn_trait_def_id,
substs: tcx.mk_substs_trait(self_ty, &[arguments_tuple.into()]),
};
ty::Binder::bind((trait_ref, sig.skip_binder().output()))
}
pub fn generator_trait_ref_and_outputs(
tcx: TyCtxt<'tcx>,
fn_trait_def_id: DefId,
self_ty: Ty<'tcx>,
sig: ty::PolyGenSig<'tcx>,
) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> {
let trait_ref = ty::TraitRef {
def_id: fn_trait_def_id,
substs: tcx.mk_substs_trait(self_ty, &[sig.skip_binder().resume_ty.into()]),
};
ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty))
}
pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
assoc_item.defaultness.is_final() && tcx.impl_defaultness(assoc_item.container.id()).is_final()
}
pub enum TupleArgumentsFlag {
Yes,
No,
}

View file

@ -0,0 +1,710 @@
use crate::infer::InferCtxt;
use crate::opaque_types::required_region_bounds;
use crate::traits;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
use rustc_span::Span;
use std::rc::Rc;
/// Returns the set of obligations needed to make `arg` well-formed.
/// If `arg` contains unresolved inference variables, this may include
/// further WF obligations. However, if `arg` IS an unresolved
/// inference variable, returns `None`, because we are not able to
/// make any progress at all. This is to prevent "livelock" where we
/// say "$0 is WF if $0 is WF".
pub fn obligations<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_id: hir::HirId,
arg: GenericArg<'tcx>,
span: Span,
) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
// Handle the "livelock" case (see comment above) by bailing out if necessary.
let arg = match arg.unpack() {
GenericArgKind::Type(ty) => {
match ty.kind {
ty::Infer(ty::TyVar(_)) => {
let resolved_ty = infcx.shallow_resolve(ty);
if resolved_ty == ty {
// No progress, bail out to prevent "livelock".
return None;
}
resolved_ty
}
_ => ty,
}
.into()
}
GenericArgKind::Const(ct) => {
match ct.val {
ty::ConstKind::Infer(infer) => {
let resolved = infcx.shallow_resolve(infer);
if resolved == infer {
// No progress.
return None;
}
infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Infer(resolved), ty: ct.ty })
}
_ => ct,
}
.into()
}
// There is nothing we have to do for lifetimes.
GenericArgKind::Lifetime(..) => return Some(Vec::new()),
};
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
wf.compute(arg);
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out);
let result = wf.normalize();
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", arg, body_id, result);
Some(result)
}
/// Returns the obligations that make this trait reference
/// well-formed. For example, if there is a trait `Set` defined like
/// `trait Set<K:Eq>`, then the trait reference `Foo: Set<Bar>` is WF
/// if `Bar: Eq`.
pub fn trait_obligations<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_id: hir::HirId,
trait_ref: &ty::TraitRef<'tcx>,
span: Span,
item: Option<&'tcx hir::Item<'tcx>>,
) -> Vec<traits::PredicateObligation<'tcx>> {
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item };
wf.compute_trait_ref(trait_ref, Elaborate::All);
wf.normalize()
}
pub fn predicate_obligations<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_id: hir::HirId,
predicate: ty::Predicate<'tcx>,
span: Span,
) -> Vec<traits::PredicateObligation<'tcx>> {
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
// It's ok to skip the binder here because wf code is prepared for it
match predicate.skip_binders() {
ty::PredicateAtom::Trait(t, _) => {
wf.compute_trait_ref(&t.trait_ref, Elaborate::None);
}
ty::PredicateAtom::RegionOutlives(..) => {}
ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => {
wf.compute(ty.into());
}
ty::PredicateAtom::Projection(t) => {
wf.compute_projection(t.projection_ty);
wf.compute(t.ty.into());
}
ty::PredicateAtom::WellFormed(arg) => {
wf.compute(arg);
}
ty::PredicateAtom::ObjectSafe(_) => {}
ty::PredicateAtom::ClosureKind(..) => {}
ty::PredicateAtom::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) => {
wf.compute(a.into());
wf.compute(b.into());
}
ty::PredicateAtom::ConstEvaluatable(def, substs) => {
let obligations = wf.nominal_obligations(def.did, substs);
wf.out.extend(obligations);
for arg in substs.iter() {
wf.compute(arg);
}
}
ty::PredicateAtom::ConstEquate(c1, c2) => {
wf.compute(c1.into());
wf.compute(c2.into());
}
}
wf.normalize()
}
struct WfPredicates<'a, 'tcx> {
infcx: &'a InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_id: hir::HirId,
span: Span,
out: Vec<traits::PredicateObligation<'tcx>>,
item: Option<&'tcx hir::Item<'tcx>>,
}
/// Controls whether we "elaborate" supertraits and so forth on the WF
/// predicates. This is a kind of hack to address #43784. The
/// underlying problem in that issue was a trait structure like:
///
/// ```
/// trait Foo: Copy { }
/// trait Bar: Foo { }
/// impl<T: Bar> Foo for T { }
/// impl<T> Bar for T { }
/// ```
///
/// Here, in the `Foo` impl, we will check that `T: Copy` holds -- but
/// we decide that this is true because `T: Bar` is in the
/// where-clauses (and we can elaborate that to include `T:
/// Copy`). This wouldn't be a problem, except that when we check the
/// `Bar` impl, we decide that `T: Foo` must hold because of the `Foo`
/// impl. And so nowhere did we check that `T: Copy` holds!
///
/// To resolve this, we elaborate the WF requirements that must be
/// proven when checking impls. This means that (e.g.) the `impl Bar
/// for T` will be forced to prove not only that `T: Foo` but also `T:
/// Copy` (which it won't be able to do, because there is no `Copy`
/// impl for `T`).
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
enum Elaborate {
All,
None,
}
fn extend_cause_with_original_assoc_item_obligation<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: &ty::TraitRef<'tcx>,
item: Option<&hir::Item<'tcx>>,
cause: &mut traits::ObligationCause<'tcx>,
pred: &ty::Predicate<'tcx>,
mut trait_assoc_items: impl Iterator<Item = &'tcx ty::AssocItem>,
) {
debug!(
"extended_cause_with_original_assoc_item_obligation {:?} {:?} {:?} {:?}",
trait_ref, item, cause, pred
);
let items = match item {
Some(hir::Item { kind: hir::ItemKind::Impl { items, .. }, .. }) => items,
_ => return,
};
let fix_span =
|impl_item_ref: &hir::ImplItemRef<'_>| match tcx.hir().impl_item(impl_item_ref.id).kind {
hir::ImplItemKind::Const(ty, _) | hir::ImplItemKind::TyAlias(ty) => ty.span,
_ => impl_item_ref.span,
};
// It is fine to skip the binder as we don't care about regions here.
match pred.skip_binders() {
ty::PredicateAtom::Projection(proj) => {
// The obligation comes not from the current `impl` nor the `trait` being implemented,
// but rather from a "second order" obligation, where an associated type has a
// projection coming from another associated type. See
// `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs` and
// `traits-assoc-type-in-supertrait-bad.rs`.
if let ty::Projection(projection_ty) = proj.ty.kind {
let trait_assoc_item = tcx.associated_item(projection_ty.item_def_id);
if let Some(impl_item_span) =
items.iter().find(|item| item.ident == trait_assoc_item.ident).map(fix_span)
{
cause.make_mut().span = impl_item_span;
}
}
}
ty::PredicateAtom::Trait(pred, _) => {
// An associated item obligation born out of the `trait` failed to be met. An example
// can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`.
debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred);
if let ty::Projection(ty::ProjectionTy { item_def_id, .. }) = pred.self_ty().kind {
if let Some(impl_item_span) = trait_assoc_items
.find(|i| i.def_id == item_def_id)
.and_then(|trait_assoc_item| {
items.iter().find(|i| i.ident == trait_assoc_item.ident).map(fix_span)
})
{
cause.make_mut().span = impl_item_span;
}
}
}
_ => {}
}
}
impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
traits::ObligationCause::new(self.span, self.body_id, code)
}
fn normalize(&mut self) -> Vec<traits::PredicateObligation<'tcx>> {
let cause = self.cause(traits::MiscObligation);
let infcx = &mut self.infcx;
let param_env = self.param_env;
let mut obligations = Vec::with_capacity(self.out.len());
for pred in &self.out {
assert!(!pred.has_escaping_bound_vars());
let mut selcx = traits::SelectionContext::new(infcx);
let i = obligations.len();
let value =
traits::normalize_to(&mut selcx, param_env, cause.clone(), pred, &mut obligations);
obligations.insert(i, value);
}
obligations
}
/// Pushes the obligations required for `trait_ref` to be WF into `self.out`.
fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) {
let tcx = self.infcx.tcx;
let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
debug!("compute_trait_ref obligations {:?}", obligations);
let cause = self.cause(traits::MiscObligation);
let param_env = self.param_env;
let item = self.item;
let extend = |obligation: traits::PredicateObligation<'tcx>| {
let mut cause = cause.clone();
if let Some(parent_trait_ref) = obligation.predicate.to_opt_poly_trait_ref() {
let derived_cause = traits::DerivedObligationCause {
parent_trait_ref,
parent_code: Rc::new(obligation.cause.code.clone()),
};
cause.make_mut().code =
traits::ObligationCauseCode::DerivedObligation(derived_cause);
}
extend_cause_with_original_assoc_item_obligation(
tcx,
trait_ref,
item,
&mut cause,
&obligation.predicate,
tcx.associated_items(trait_ref.def_id).in_definition_order(),
);
traits::Obligation::new(cause, param_env, obligation.predicate)
};
if let Elaborate::All = elaborate {
let implied_obligations = traits::util::elaborate_obligations(tcx, obligations);
let implied_obligations = implied_obligations.map(extend);
self.out.extend(implied_obligations);
} else {
self.out.extend(obligations);
}
let tcx = self.tcx();
self.out.extend(
trait_ref
.substs
.iter()
.enumerate()
.filter(|(_, arg)| {
matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
})
.filter(|(_, arg)| !arg.has_escaping_bound_vars())
.map(|(i, arg)| {
let mut new_cause = cause.clone();
// The first subst is the self ty - use the correct span for it.
if i == 0 {
if let Some(hir::ItemKind::Impl { self_ty, .. }) = item.map(|i| &i.kind) {
new_cause.make_mut().span = self_ty.span;
}
}
traits::Obligation::new(
new_cause,
param_env,
ty::PredicateAtom::WellFormed(arg).to_predicate(tcx),
)
}),
);
}
/// Pushes the obligations required for `trait_ref::Item` to be WF
/// into `self.out`.
fn compute_projection(&mut self, data: ty::ProjectionTy<'tcx>) {
// A projection is well-formed if (a) the trait ref itself is
// WF and (b) the trait-ref holds. (It may also be
// normalizable and be WF that way.)
let trait_ref = data.trait_ref(self.infcx.tcx);
self.compute_trait_ref(&trait_ref, Elaborate::None);
if !data.has_escaping_bound_vars() {
let predicate = trait_ref.without_const().to_predicate(self.infcx.tcx);
let cause = self.cause(traits::ProjectionWf(data));
self.out.push(traits::Obligation::new(cause, self.param_env, predicate));
}
}
fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) {
if !subty.has_escaping_bound_vars() {
let cause = self.cause(cause);
let trait_ref = ty::TraitRef {
def_id: self.infcx.tcx.require_lang_item(LangItem::Sized, None),
substs: self.infcx.tcx.mk_substs_trait(subty, &[]),
};
self.out.push(traits::Obligation::new(
cause,
self.param_env,
trait_ref.without_const().to_predicate(self.infcx.tcx),
));
}
}
/// Pushes all the predicates needed to validate that `ty` is WF into `out`.
fn compute(&mut self, arg: GenericArg<'tcx>) {
let mut walker = arg.walk();
let param_env = self.param_env;
while let Some(arg) = walker.next() {
let ty = match arg.unpack() {
GenericArgKind::Type(ty) => ty,
// No WF constraints for lifetimes being present, any outlives
// obligations are handled by the parent (e.g. `ty::Ref`).
GenericArgKind::Lifetime(_) => continue,
GenericArgKind::Const(constant) => {
match constant.val {
ty::ConstKind::Unevaluated(def, substs, promoted) => {
assert!(promoted.is_none());
let obligations = self.nominal_obligations(def.did, substs);
self.out.extend(obligations);
let predicate = ty::PredicateAtom::ConstEvaluatable(def, substs)
.to_predicate(self.tcx());
let cause = self.cause(traits::MiscObligation);
self.out.push(traits::Obligation::new(
cause,
self.param_env,
predicate,
));
}
ty::ConstKind::Infer(infer) => {
let resolved = self.infcx.shallow_resolve(infer);
// the `InferConst` changed, meaning that we made progress.
if resolved != infer {
let cause = self.cause(traits::MiscObligation);
let resolved_constant = self.infcx.tcx.mk_const(ty::Const {
val: ty::ConstKind::Infer(resolved),
..*constant
});
self.out.push(traits::Obligation::new(
cause,
self.param_env,
ty::PredicateAtom::WellFormed(resolved_constant.into())
.to_predicate(self.tcx()),
));
}
}
ty::ConstKind::Error(_)
| ty::ConstKind::Param(_)
| ty::ConstKind::Bound(..)
| ty::ConstKind::Placeholder(..) => {
// These variants are trivially WF, so nothing to do here.
}
ty::ConstKind::Value(..) => {
// FIXME: Enforce that values are structurally-matchable.
}
}
continue;
}
};
match ty.kind {
ty::Bool
| ty::Char
| ty::Int(..)
| ty::Uint(..)
| ty::Float(..)
| ty::Error(_)
| ty::Str
| ty::GeneratorWitness(..)
| ty::Never
| ty::Param(_)
| ty::Bound(..)
| ty::Placeholder(..)
| ty::Foreign(..) => {
// WfScalar, WfParameter, etc
}
// Can only infer to `ty::Int(_) | ty::Uint(_)`.
ty::Infer(ty::IntVar(_)) => {}
// Can only infer to `ty::Float(_)`.
ty::Infer(ty::FloatVar(_)) => {}
ty::Slice(subty) => {
self.require_sized(subty, traits::SliceOrArrayElem);
}
ty::Array(subty, _) => {
self.require_sized(subty, traits::SliceOrArrayElem);
// Note that we handle the len is implicitly checked while walking `arg`.
}
ty::Tuple(ref tys) => {
if let Some((_last, rest)) = tys.split_last() {
for elem in rest {
self.require_sized(elem.expect_ty(), traits::TupleElem);
}
}
}
ty::RawPtr(_) => {
// Simple cases that are WF if their type args are WF.
}
ty::Projection(data) => {
walker.skip_current_subtree(); // Subtree handled by compute_projection.
self.compute_projection(data);
}
ty::Adt(def, substs) => {
// WfNominalType
let obligations = self.nominal_obligations(def.did, substs);
self.out.extend(obligations);
}
ty::FnDef(did, substs) => {
let obligations = self.nominal_obligations(did, substs);
self.out.extend(obligations);
}
ty::Ref(r, rty, _) => {
// WfReference
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
self.out.push(traits::Obligation::new(
cause,
param_env,
ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(rty, r))
.to_predicate(self.tcx()),
));
}
}
ty::Generator(..) => {
// Walk ALL the types in the generator: this will
// include the upvar types as well as the yield
// type. Note that this is mildly distinct from
// the closure case, where we have to be careful
// about the signature of the closure. We don't
// have the problem of implied bounds here since
// generators don't take arguments.
}
ty::Closure(_, substs) => {
// Only check the upvar types for WF, not the rest
// of the types within. This is needed because we
// capture the signature and it may not be WF
// without the implied bounds. Consider a closure
// like `|x: &'a T|` -- it may be that `T: 'a` is
// not known to hold in the creator's context (and
// indeed the closure may not be invoked by its
// creator, but rather turned to someone who *can*
// verify that).
//
// The special treatment of closures here really
// ought not to be necessary either; the problem
// is related to #25860 -- there is no way for us
// to express a fn type complete with the implied
// bounds that it is assuming. I think in reality
// the WF rules around fn are a bit messed up, and
// that is the rot problem: `fn(&'a T)` should
// probably always be WF, because it should be
// shorthand for something like `where(T: 'a) {
// fn(&'a T) }`, as discussed in #25860.
//
// Note that we are also skipping the generic
// types. This is consistent with the `outlives`
// code, but anyway doesn't matter: within the fn
// body where they are created, the generics will
// always be WF, and outside of that fn body we
// are not directly inspecting closure types
// anyway, except via auto trait matching (which
// only inspects the upvar types).
walker.skip_current_subtree(); // subtree handled below
for upvar_ty in substs.as_closure().upvar_tys() {
// FIXME(eddyb) add the type to `walker` instead of recursing.
self.compute(upvar_ty.into());
}
}
ty::FnPtr(_) => {
// let the loop iterate into the argument/return
// types appearing in the fn signature
}
ty::Opaque(did, substs) => {
// all of the requirements on type parameters
// should've been checked by the instantiation
// of whatever returned this exact `impl Trait`.
// for named opaque `impl Trait` types we still need to check them
if ty::is_impl_trait_defn(self.infcx.tcx, did).is_none() {
let obligations = self.nominal_obligations(did, substs);
self.out.extend(obligations);
}
}
ty::Dynamic(data, r) => {
// WfObject
//
// Here, we defer WF checking due to higher-ranked
// regions. This is perhaps not ideal.
self.from_object_ty(ty, data, r);
// FIXME(#27579) RFC also considers adding trait
// obligations that don't refer to Self and
// checking those
let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
if !defer_to_coercion {
let cause = self.cause(traits::MiscObligation);
let component_traits = data.auto_traits().chain(data.principal_def_id());
let tcx = self.tcx();
self.out.extend(component_traits.map(|did| {
traits::Obligation::new(
cause.clone(),
param_env,
ty::PredicateAtom::ObjectSafe(did).to_predicate(tcx),
)
}));
}
}
// Inference variables are the complicated case, since we don't
// know what type they are. We do two things:
//
// 1. Check if they have been resolved, and if so proceed with
// THAT type.
// 2. If not, we've at least simplified things (e.g., we went
// from `Vec<$0>: WF` to `$0: WF`), so we can
// register a pending obligation and keep
// moving. (Goal is that an "inductive hypothesis"
// is satisfied to ensure termination.)
// See also the comment on `fn obligations`, describing "livelock"
// prevention, which happens before this can be reached.
ty::Infer(_) => {
let ty = self.infcx.shallow_resolve(ty);
if let ty::Infer(ty::TyVar(_)) = ty.kind {
// Not yet resolved, but we've made progress.
let cause = self.cause(traits::MiscObligation);
self.out.push(traits::Obligation::new(
cause,
param_env,
ty::PredicateAtom::WellFormed(ty.into()).to_predicate(self.tcx()),
));
} else {
// Yes, resolved, proceed with the result.
// FIXME(eddyb) add the type to `walker` instead of recursing.
self.compute(ty.into());
}
}
}
}
}
fn nominal_obligations(
&mut self,
def_id: DefId,
substs: SubstsRef<'tcx>,
) -> Vec<traits::PredicateObligation<'tcx>> {
let predicates = self.infcx.tcx.predicates_of(def_id).instantiate(self.infcx.tcx, substs);
predicates
.predicates
.into_iter()
.zip(predicates.spans.into_iter())
.map(|(pred, span)| {
let cause = self.cause(traits::BindingObligation(def_id, span));
traits::Obligation::new(cause, self.param_env, pred)
})
.filter(|pred| !pred.has_escaping_bound_vars())
.collect()
}
fn from_object_ty(
&mut self,
ty: Ty<'tcx>,
data: ty::Binder<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>,
region: ty::Region<'tcx>,
) {
// Imagine a type like this:
//
// trait Foo { }
// trait Bar<'c> : 'c { }
//
// &'b (Foo+'c+Bar<'d>)
// ^
//
// In this case, the following relationships must hold:
//
// 'b <= 'c
// 'd <= 'c
//
// The first conditions is due to the normal region pointer
// rules, which say that a reference cannot outlive its
// referent.
//
// The final condition may be a bit surprising. In particular,
// you may expect that it would have been `'c <= 'd`, since
// usually lifetimes of outer things are conservative
// approximations for inner things. However, it works somewhat
// differently with trait objects: here the idea is that if the
// user specifies a region bound (`'c`, in this case) it is the
// "master bound" that *implies* that bounds from other traits are
// all met. (Remember that *all bounds* in a type like
// `Foo+Bar+Zed` must be met, not just one, hence if we write
// `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and
// 'y.)
//
// Note: in fact we only permit builtin traits, not `Bar<'d>`, I
// am looking forward to the future here.
if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() {
let implicit_bounds = object_region_bounds(self.infcx.tcx, data);
let explicit_bound = region;
self.out.reserve(implicit_bounds.len());
for implicit_bound in implicit_bounds {
let cause = self.cause(traits::ObjectTypeBound(ty, explicit_bound));
let outlives =
ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound));
self.out.push(traits::Obligation::new(
cause,
self.param_env,
outlives.to_predicate(self.infcx.tcx),
));
}
}
}
}
/// Given an object type like `SomeTrait + Send`, computes the lifetime
/// bounds that must hold on the elided self type. These are derived
/// from the declarations of `SomeTrait`, `Send`, and friends -- if
/// they declare `trait SomeTrait : 'static`, for example, then
/// `'static` would appear in the list. The hard work is done by
/// `infer::required_region_bounds`, see that for more information.
pub fn object_region_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
existential_predicates: ty::Binder<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>,
) -> Vec<ty::Region<'tcx>> {
// Since we don't actually *know* the self type for an object,
// this "open(err)" serves as a kind of dummy standin -- basically
// a placeholder type.
let open_ty = tcx.mk_ty_infer(ty::FreshTy(0));
let predicates = existential_predicates.iter().filter_map(|predicate| {
if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() {
None
} else {
Some(predicate.with_self_ty(tcx, open_ty))
}
});
required_region_bounds(tcx, open_ty, predicates)
}