Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the recorded types in MIR to determine generator auto-trait implementations #65782

Closed
wants to merge 15 commits into from
Closed
4 changes: 4 additions & 0 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ impl HirId {
}
}

CloneTypeFoldableImpls! {
HirId,
}

impl rustc_serialize::UseSpecializedEncodable for HirId {
fn default_encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
let HirId {
Expand Down
5 changes: 4 additions & 1 deletion src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use syntax::symbol::Symbol;
use syntax_pos::{Span, DUMMY_SP};

pub use crate::mir::interpret::AssertMessage;
pub use crate::mir::cache::{BodyCache, ReadOnlyBodyCache};
pub use crate::mir::cache::{Cache, BodyCache, ReadOnlyBodyCache};
pub use crate::read_only;

mod cache;
Expand Down Expand Up @@ -117,6 +117,8 @@ pub struct Body<'tcx> {
/// to be created.
pub generator_kind: Option<GeneratorKind>,

pub generator_interior_tys: Option<Vec<Ty<'tcx>>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These shouldn't be stored in the MIR, and should instead be the return value of the query.


/// Declarations of locals.
///
/// The first local is the return value pointer, followed by `arg_count`
Expand Down Expand Up @@ -184,6 +186,7 @@ impl<'tcx> Body<'tcx> {
generator_drop: None,
generator_layout: None,
generator_kind,
generator_interior_tys: None,
local_decls,
user_type_annotations,
arg_count,
Expand Down
9 changes: 9 additions & 0 deletions src/librustc/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ rustc_queries! {
/// unreachable code.
query mir_built(_: DefId) -> &'tcx Steal<mir::BodyCache<'tcx>> {}

/// Compute the generator interior types for a given `DefId`
/// (if it corresponds to a generator), for use in determining
/// generator auto trait implementation
query mir_generator_interior(_: DefId) -> &'tcx Steal<mir::BodyCache<'tcx>> {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't need to modify the MIR so it should return a &'tcx List<Ty<'tcx>> (and can be renamed).


/// Fetch the MIR for a given `DefId` up till the point where it is
/// ready for const evaluation.
///
Expand Down Expand Up @@ -931,6 +936,10 @@ rustc_queries! {
query all_traits(_: CrateNum) -> &'tcx [DefId] {
desc { "fetching all foreign and local traits" }
}

query uses_generator_mir_traits(_: CrateNum) -> bool {
desc { "determining whether crate uses generator MIR traits" }
}
}

Linking {
Expand Down
43 changes: 42 additions & 1 deletion src/librustc/traits/chalk_fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,39 @@ use crate::traits::{
SelectionError,
};
use crate::traits::query::NoSolution;
use crate::traits::DelayedGenerators;
use crate::infer::InferCtxt;
use crate::infer::canonical::{Canonical, OriginalQueryValues};
use crate::ty::{self, Ty};
use crate::ty::{self, Ty, Predicate};
use rustc_data_structures::fx::FxHashSet;

pub type CanonicalGoal<'tcx> = Canonical<'tcx, InEnvironment<'tcx, ty::Predicate<'tcx>>>;

pub struct FulfillmentContext<'tcx> {
obligations: FxHashSet<InEnvironment<'tcx, PredicateObligation<'tcx>>>,
// See FulfillmentContext for more details about delayed generator
// witness obligations
delayed_generator_witnesses: DelayedGenerators<'tcx>,
has_delayed_generator_witnesses: bool
}

impl FulfillmentContext<'tcx> {
crate fn new() -> Self {
FulfillmentContext {
obligations: FxHashSet::default(),
delayed_generator_witnesses: None,
has_delayed_generator_witnesses: false
}
}

crate fn with_delayed_generator_witness() -> Self {
FulfillmentContext {
obligations: FxHashSet::default(),
delayed_generator_witnesses: Some(Vec::new()),
has_delayed_generator_witnesses: true
}

}
}

fn in_environment(
Expand Down Expand Up @@ -108,6 +124,24 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
goal: obligation.goal.predicate,
}, &mut orig_values);

// See FulfillmentContext for more details about this
if self.has_delayed_generator_witnesses {

if let Predicate::Trait(p) = obligation.goal.predicate {
if let ty::GeneratorWitness(..) = p.skip_binder().self_ty().kind {
if infcx.tcx.trait_is_auto(p.def_id()) {
debug!("delaying generator witness predicate {:?}",
obligation.goal.predicate);

self.delayed_generator_witnesses.as_mut()
.expect("Already consumed generator witnesses!")
.push(obligation.goal);
continue;
}
}
}
}

match infcx.tcx.evaluate_goal(canonical_goal) {
Ok(response) => {
if response.is_proven() {
Expand Down Expand Up @@ -165,4 +199,11 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.obligations.iter().map(|obligation| obligation.goal.clone()).collect()
}

fn delayed_generator_obligations(&mut self) -> Option<Vec<PredicateObligation<'tcx>>> {
if !self.has_delayed_generator_witnesses {
panic!("Tried to retrieve delayed generators in wrong mode!")
}
self.delayed_generator_witnesses.take()
}
}
40 changes: 40 additions & 0 deletions src/librustc/traits/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ pub trait TraitEngine<'tcx>: 'tcx {
) -> Result<(), Vec<FulfillmentError<'tcx>>>;

fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;

/// Retrieves the list of delayed generator witness predicates
/// stored by this `TraitEngine`. This `TraitEngine` must have been
/// created by `TraitEngine::with_delayed_generator_witness` - otherwise,
/// this method will panic.
///
/// Calling this method consumes the underling `Vec` - subsequent calls
/// will return `None`.
///
/// This method *MUST* be called on a `TraitEngine` created by
/// `with_delayed_generator_witness` - if the `TraitEngine` is dropped
/// without this method being called, a panic will occur. This ensures
/// that the caller explicitly acknowledges these stored predicates -
/// failure to do so will result in unsound code being accepted by
/// the compiler.
fn delayed_generator_obligations(&mut self) -> Option<Vec<PredicateObligation<'tcx>>>;
}

pub trait TraitEngineExt<'tcx> {
Expand Down Expand Up @@ -85,4 +101,28 @@ impl dyn TraitEngine<'tcx> {
Box::new(FulfillmentContext::new())
}
}

/// Creates a `TraitEngine` in a special 'delay generator witness' mode.
/// This imposes additional requirements for the caller in order to avoid
/// accepting unsound code, and should only be used by `FnCtxt`. All other
/// users of `TraitEngine` should use `TraitEngine::new`.
///
/// A `TraitEngine` returned by this constructor will not attempt
/// to resolve any `GeneratorWitness` predicates involving auto traits,
/// Specifically, predicates of the form:
///
/// `<GeneratorWitness>: MyTrait` where `MyTrait` is an auto-trait
/// will be stored for later retrieval by `delayed_generator_obligations`.
/// The caller of this code *MUST* register these predicates with a
/// regular `TraitEngine` (created with `TraitEngine::new`) at some point.
/// Otherwise, these predicates will never be evaluated, resulting in
/// unsound programs being accepted by the compiler.
pub fn with_delayed_generator_witness(tcx: TyCtxt<'tcx>) -> Box<Self> {
if tcx.sess.opts.debugging_opts.chalk {
Box::new(ChalkFulfillmentContext::with_delayed_generator_witness())
} else {
Box::new(FulfillmentContext::with_delayed_generator_witness())
}

}
}
2 changes: 1 addition & 1 deletion src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2181,7 +2181,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
ty::Adt(ty::AdtDef { did, .. }, ..) if
self.tcx.is_diagnostic_item(sym::gen_future, *did) => {},
ty::Generator(did, ..) => generator = generator.or(Some(did)),
ty::GeneratorWitness(_) | ty::Opaque(..) => {},
ty::GeneratorWitness(..) | ty::Opaque(..) => {},
_ => return false,
}

Expand Down
60 changes: 57 additions & 3 deletions src/librustc/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ use super::{FulfillmentError, FulfillmentErrorCode};
use super::{ObligationCause, PredicateObligation};
use super::project;
use super::select::SelectionContext;
use super::{Unimplemented, ConstEvalFailure};
use super::{Unimplemented, ConstEvalFailure, DelayedGenerators};

impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
type Predicate = ty::Predicate<'tcx>;

fn as_predicate(&self) -> &Self::Predicate { &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
Expand Down Expand Up @@ -59,7 +60,17 @@ pub struct FulfillmentContext<'tcx> {
// 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
usable_in_snapshot: bool,

// Whether or not we show delay the selection of predicates
// involving `ty::GeneratorWitness`.
// This is used by `FnCtxt` to delay the checking of
// `GeneratorWitness` predicates, since generator MIR is
// not available when `FnCxtxt` is run.
has_delayed_generator_witness: bool,
// The delayed generator witness predicates. This is only
// used when `has_delayed_generator_witness:` is `true`.
delayed_generator_witness: DelayedGenerators<'tcx>,
}

#[derive(Clone, Debug)]
Expand All @@ -79,6 +90,18 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
predicates: ObligationForest::new(),
register_region_obligations: true,
usable_in_snapshot: false,
delayed_generator_witness: None,
has_delayed_generator_witness: false,
}
}

pub fn with_delayed_generator_witness() -> FulfillmentContext<'tcx> {
FulfillmentContext {
predicates: ObligationForest::new(),
register_region_obligations: true,
usable_in_snapshot: false,
delayed_generator_witness: Some(Vec::new()),
has_delayed_generator_witness: true,
}
}

Expand All @@ -87,14 +110,18 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
predicates: ObligationForest::new(),
register_region_obligations: true,
usable_in_snapshot: true,
delayed_generator_witness: None,
has_delayed_generator_witness: false,
}
}

pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> {
FulfillmentContext {
predicates: ObligationForest::new(),
register_region_obligations: false,
usable_in_snapshot: false
usable_in_snapshot: false,
delayed_generator_witness: None,
has_delayed_generator_witness: false,
}
}

Expand All @@ -112,6 +139,8 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {

// Process pending obligations.
let outcome = self.predicates.process_obligations(&mut FulfillProcessor {
has_delayed_generator_witness: self.has_delayed_generator_witness,
delayed_generator_witness: &mut self.delayed_generator_witness,
selcx,
register_region_obligations: self.register_region_obligations
}, DoCompleted::No);
Expand Down Expand Up @@ -226,10 +255,19 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.predicates.map_pending_obligations(|o| o.obligation.clone())
}

fn delayed_generator_obligations(&mut self) -> Option<Vec<PredicateObligation<'tcx>>> {
if !self.has_delayed_generator_witness {
panic!("Tried to retrieve delayed generators in wrong mode!")
}
self.delayed_generator_witness.take()
}
}

struct FulfillProcessor<'a, 'b, 'tcx> {
selcx: &'a mut SelectionContext<'b, 'tcx>,
has_delayed_generator_witness: bool,
delayed_generator_witness: &'a mut DelayedGenerators<'tcx>,
register_region_obligations: bool,
}

Expand Down Expand Up @@ -309,6 +347,21 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
ty::Predicate::Trait(ref data) => {
let trait_obligation = obligation.with(data.clone());


if self.has_delayed_generator_witness &&
self.selcx.tcx().trait_is_auto(data.def_id()) {
// Ok to skip binder - bound regions do not affect whether self
// type is a `GeneratorWitness
if let ty::GeneratorWitness(..) = data.skip_binder().self_ty().kind {
debug!("delaying generator witness predicate `{:?}` at depth {}",
data, obligation.recursion_depth);
self.delayed_generator_witness.as_mut()
.expect("Delayed generator witnesses already consumed!")
.push(obligation.clone());
return ProcessResult::Changed(vec![])
}
}

if data.is_global() {
// no type variables present, can use evaluation for better caching.
// FIXME: consider caching errors too.
Expand All @@ -319,6 +372,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
}
}


match self.selcx.select(&trait_obligation) {
Ok(Some(vtable)) => {
debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)",
Expand Down
Loading