mv compiler to compiler/
This commit is contained in:
parent
db534b3ac2
commit
9e5f7d5631
1686 changed files with 941 additions and 1051 deletions
233
compiler/rustc_trait_selection/src/autoderef.rs
Normal file
233
compiler/rustc_trait_selection/src/autoderef.rs
Normal 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();
|
||||
}
|
||||
}
|
182
compiler/rustc_trait_selection/src/infer.rs
Normal file
182
compiler/rustc_trait_selection/src/infer.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
34
compiler/rustc_trait_selection/src/lib.rs
Normal file
34
compiler/rustc_trait_selection/src/lib.rs
Normal 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;
|
1284
compiler/rustc_trait_selection/src/opaque_types.rs
Normal file
1284
compiler/rustc_trait_selection/src/opaque_types.rs
Normal file
File diff suppressed because it is too large
Load diff
881
compiler/rustc_trait_selection/src/traits/auto_trait.rs
Normal file
881
compiler/rustc_trait_selection/src/traits/auto_trait.rs
Normal 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(®ion_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))
|
||||
}
|
||||
}
|
267
compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
Normal file
267
compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
Normal 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()
|
||||
}
|
||||
}
|
126
compiler/rustc_trait_selection/src/traits/codegen/mod.rs
Normal file
126
compiler/rustc_trait_selection/src/traits/codegen/mod.rs
Normal 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)
|
||||
}
|
577
compiler/rustc_trait_selection/src/traits/coherence.rs
Normal file
577
compiler/rustc_trait_selection/src/traits/coherence.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
18
compiler/rustc_trait_selection/src/traits/engine.rs
Normal file
18
compiler/rustc_trait_selection/src/traits/engine.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
1949
compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Normal file
1949
compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
668
compiler/rustc_trait_selection/src/traits/fulfill.rs
Normal file
668
compiler/rustc_trait_selection/src/traits/fulfill.rs
Normal 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)
|
||||
}
|
74
compiler/rustc_trait_selection/src/traits/misc.rs
Normal file
74
compiler/rustc_trait_selection/src/traits/misc.rs
Normal 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(())
|
||||
})
|
||||
}
|
563
compiler/rustc_trait_selection/src/traits/mod.rs
Normal file
563
compiler/rustc_trait_selection/src/traits/mod.rs
Normal 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
|
||||
};
|
||||
}
|
789
compiler/rustc_trait_selection/src/traits/object_safety.rs
Normal file
789
compiler/rustc_trait_selection/src/traits/object_safety.rs
Normal 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(¬e);
|
||||
}
|
||||
(Some(_), Some((note, Some((sugg, span))))) => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
¬e,
|
||||
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 };
|
||||
}
|
385
compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
Normal file
385
compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
Normal 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()
|
||||
}
|
||||
}
|
1581
compiler/rustc_trait_selection/src/traits/project.rs
Normal file
1581
compiler/rustc_trait_selection/src/traits/project.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
pub use rustc_middle::traits::query::{
|
||||
CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult,
|
||||
};
|
15
compiler/rustc_trait_selection/src/traits/query/mod.rs
Normal file
15
compiler/rustc_trait_selection/src/traits/query/mod.rs
Normal 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::*;
|
207
compiler/rustc_trait_selection/src/traits/query/normalize.rs
Normal file
207
compiler/rustc_trait_selection/src/traits/query/normalize.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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(¶m_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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)),
|
||||
®ion_constraint_data,
|
||||
);
|
||||
|
||||
if region_constraints.is_empty() {
|
||||
Ok((value, None))
|
||||
} else {
|
||||
Ok((value, Some(Rc::new(region_constraints))))
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
136
compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
Normal file
136
compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
Normal 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))
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
}
|
810
compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Normal file
810
compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Normal 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 })
|
||||
}
|
||||
}
|
2436
compiler/rustc_trait_selection/src/traits/select/mod.rs
Normal file
2436
compiler/rustc_trait_selection/src/traits/select/mod.rs
Normal file
File diff suppressed because it is too large
Load diff
518
compiler/rustc_trait_selection/src/traits/specialize/mod.rs
Normal file
518
compiler/rustc_trait_selection/src/traits/specialize/mod.rs
Normal 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)
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
281
compiler/rustc_trait_selection/src/traits/structural_match.rs
Normal file
281
compiler/rustc_trait_selection/src/traits/structural_match.rs
Normal 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)
|
||||
})
|
||||
};
|
||||
}
|
363
compiler/rustc_trait_selection/src/traits/util.rs
Normal file
363
compiler/rustc_trait_selection/src/traits/util.rs
Normal 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,
|
||||
}
|
710
compiler/rustc_trait_selection/src/traits/wf.rs
Normal file
710
compiler/rustc_trait_selection/src/traits/wf.rs
Normal 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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue