about summary refs log tree commit diff
path: root/src/librustc_traits/lowering/environment.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustc_traits/lowering/environment.rs')
-rw-r--r--src/librustc_traits/lowering/environment.rs302
1 files changed, 302 insertions, 0 deletions
diff --git a/src/librustc_traits/lowering/environment.rs b/src/librustc_traits/lowering/environment.rs
new file mode 100644
index 00000000000..54f0c6e8da7
--- /dev/null
+++ b/src/librustc_traits/lowering/environment.rs
@@ -0,0 +1,302 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::traits::{
+    Clause,
+    Clauses,
+    DomainGoal,
+    FromEnv,
+    ProgramClause,
+    ProgramClauseCategory,
+    Environment,
+};
+use rustc::ty::{self, TyCtxt, Ty};
+use rustc::hir::def_id::DefId;
+use rustc_data_structures::fx::FxHashSet;
+use super::Lower;
+use std::iter;
+
+struct ClauseVisitor<'set, 'a, 'tcx: 'a + 'set> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    round: &'set mut FxHashSet<Clause<'tcx>>,
+}
+
+impl ClauseVisitor<'set, 'a, 'tcx> {
+    fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, round: &'set mut FxHashSet<Clause<'tcx>>) -> Self {
+        ClauseVisitor {
+            tcx,
+            round,
+        }
+    }
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) {
+        match ty.sty {
+            ty::Projection(data) => {
+                self.round.extend(
+                    self.tcx.program_clauses_for(data.item_def_id)
+                        .iter()
+                        .filter(|c| c.category() == ProgramClauseCategory::ImpliedBound)
+                        .cloned()
+                );
+            }
+
+            // forall<'a, T> { `Outlives(T: 'a) :- FromEnv(&'a T)` }
+            ty::Ref(..) => {
+                use rustc::hir;
+
+                let region = self.tcx.mk_region(
+                    ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(0))
+                );
+                let ty = self.tcx.mk_ty(
+                    ty::Bound(ty::BoundTy::new(ty::INNERMOST, ty::BoundVar::from_u32(1)))
+                );
+
+                let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut {
+                    ty,
+                    mutbl: hir::Mutability::MutImmutable,
+                });
+                let from_env = DomainGoal::FromEnv(FromEnv::Ty(ref_ty));
+
+                let clause = ProgramClause {
+                    goal: ty::OutlivesPredicate(ty, region).lower(),
+                    hypotheses: self.tcx.mk_goals(
+                        iter::once(self.tcx.mk_goal(from_env.into_goal()))
+                    ),
+                    category: ProgramClauseCategory::ImpliedBound,
+                };
+                let clause = Clause::ForAll(ty::Binder::bind(clause));
+                self.round.insert(clause);
+            }
+
+            ty::Dynamic(..) => {
+                // FIXME: trait object rules are not yet implemented
+            }
+
+            ty::Adt(def, ..) => {
+                self.round.extend(
+                    self.tcx.program_clauses_for(def.did)
+                        .iter()
+                        .filter(|c| c.category() == ProgramClauseCategory::ImpliedBound)
+                        .cloned()
+                );
+            }
+
+            ty::Foreign(def_id) |
+            ty::FnDef(def_id, ..) |
+            ty::Closure(def_id, ..) |
+            ty::Generator(def_id, ..) |
+            ty::Opaque(def_id, ..) => {
+                self.round.extend(
+                    self.tcx.program_clauses_for(def_id)
+                        .iter()
+                        .filter(|c| c.category() == ProgramClauseCategory::ImpliedBound)
+                        .cloned()
+                );
+            }
+
+            ty::Bool |
+            ty::Char |
+            ty::Int(..) |
+            ty::Uint(..) |
+            ty::Float(..) |
+            ty::Str |
+            ty::Array(..) |
+            ty::Slice(..) |
+            ty::RawPtr(..) |
+            ty::FnPtr(..) |
+            ty::Tuple(..) |
+            ty::Never |
+            ty::Infer(..) |
+            ty::Bound(..) => (),
+
+            ty::GeneratorWitness(..) |
+            ty::UnnormalizedProjection(..) |
+            ty::Param(..) |
+            ty::Error => {
+                bug!("unexpected type {:?}", ty);
+            }
+        }
+    }
+
+    fn visit_from_env(&mut self, from_env: FromEnv<'tcx>) {
+        match from_env {
+            FromEnv::Trait(predicate) => {
+                self.round.extend(
+                    self.tcx.program_clauses_for(predicate.def_id())
+                        .iter()
+                        .filter(|c| c.category() == ProgramClauseCategory::ImpliedBound)
+                        .cloned()
+                );
+            }
+
+            FromEnv::Ty(ty) => self.visit_ty(ty),
+        }
+    }
+
+    fn visit_domain_goal(&mut self, domain_goal: DomainGoal<'tcx>) {
+        // The only domain goals we can find in an environment are:
+        // * `DomainGoal::Holds(..)`
+        // * `DomainGoal::FromEnv(..)`
+        // The former do not lead to any implied bounds. So we only need
+        // to visit the latter.
+        if let DomainGoal::FromEnv(from_env) = domain_goal {
+            self.visit_from_env(from_env);
+        }
+    }
+
+    fn visit_program_clause(&mut self, clause: ProgramClause<'tcx>) {
+        self.visit_domain_goal(clause.goal);
+        // No need to visit `clause.hypotheses`: they are always of the form
+        // `FromEnv(...)` and were visited at a previous round.
+    }
+
+    fn visit_clause(&mut self, clause: Clause<'tcx>) {
+        match clause {
+            Clause::Implies(clause) => self.visit_program_clause(clause),
+            Clause::ForAll(clause) => self.visit_program_clause(*clause.skip_binder()),
+        }
+    }
+}
+
+crate fn program_clauses_for_env<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    environment: Environment<'tcx>,
+) -> Clauses<'tcx> {
+    debug!("program_clauses_for_env(environment={:?})", environment);
+
+    let mut last_round = FxHashSet::default();
+    {
+        let mut visitor = ClauseVisitor::new(tcx, &mut last_round);
+        for &clause in environment.clauses {
+            visitor.visit_clause(clause);
+        }
+    }
+
+    let mut closure = last_round.clone();
+    let mut next_round = FxHashSet::default();
+    while !last_round.is_empty() {
+        let mut visitor = ClauseVisitor::new(tcx, &mut next_round);
+        for clause in last_round.drain() {
+            visitor.visit_clause(clause);
+        }
+        last_round.extend(
+            next_round.drain().filter(|&clause| closure.insert(clause))
+        );
+    }
+
+    debug!("program_clauses_for_env: closure = {:#?}", closure);
+
+    return tcx.mk_clauses(
+        closure.into_iter()
+    );
+}
+
+crate fn environment<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    def_id: DefId
+) -> ty::Binder<Environment<'tcx>> {
+    use super::{Lower, IntoFromEnvGoal};
+    use rustc::hir::{Node, TraitItemKind, ImplItemKind, ItemKind, ForeignItemKind};
+    use rustc::ty::subst::{Subst, Substs};
+
+    // 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);
+    }
+
+    let bound_vars = Substs::bound_vars_for_item(tcx, def_id);
+
+    // 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(|predicate| predicate.lower())
+        .map(|predicate| predicate.subst(tcx, bound_vars))
+        .map(|domain_goal| domain_goal.map_bound(|bound| bound.into_from_env_goal()))
+        .map(|domain_goal| domain_goal.map_bound(|bound| bound.into_program_clause()))
+
+        // `ForAll` because each `domain_goal` is a `PolyDomainGoal` and
+        // could bound lifetimes.
+        .map(Clause::ForAll);
+
+    let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
+    let node = tcx.hir.get(node_id);
+
+    let mut is_fn = false;
+    let mut is_impl = false;
+    match node {
+        Node::TraitItem(item) => match item.node {
+            TraitItemKind::Method(..) => is_fn = true,
+            _ => (),
+        }
+
+        Node::ImplItem(item) => match item.node {
+            ImplItemKind::Method(..) => is_fn = true,
+            _ => (),
+        }
+
+        Node::Item(item) => match item.node {
+            ItemKind::Impl(..) => is_impl = true,
+            ItemKind::Fn(..) => is_fn = true,
+            _ => (),
+        }
+
+        Node::ForeignItem(item) => match item.node {
+            ForeignItemKind::Fn(..) => is_fn = true,
+            _ => (),
+        }
+
+        // FIXME: closures?
+        _ => (),
+    }
+
+    let mut input_tys = FxHashSet::default();
+
+    // In an impl, we assume that the header trait ref and all its constituents
+    // are well-formed.
+    if is_impl {
+        let trait_ref = tcx.impl_trait_ref(def_id)
+            .expect("not an impl")
+            .subst(tcx, bound_vars);
+
+        input_tys.extend(
+            trait_ref.substs.types().flat_map(|ty| ty.walk())
+        );
+    }
+
+    // In an fn, we assume that the arguments and all their constituents are
+    // well-formed.
+    if is_fn {
+        // `skip_binder` because we move region parameters to the root binder,
+        // restored in the return type of this query
+        let fn_sig = tcx.fn_sig(def_id).skip_binder().subst(tcx, bound_vars);
+
+        input_tys.extend(
+            fn_sig.inputs().iter().flat_map(|ty| ty.walk())
+        );
+    }
+
+    let clauses = clauses.chain(
+        input_tys.into_iter()
+            // Filter out type parameters
+            .filter(|ty| match ty.sty {
+                ty::Bound(..) => false,
+                _ => true,
+            })
+            .map(|ty| DomainGoal::FromEnv(FromEnv::Ty(ty)))
+            .map(|domain_goal| domain_goal.into_program_clause())
+            .map(Clause::Implies)
+    );
+
+    ty::Binder::bind(Environment {
+        clauses: tcx.mk_clauses(clauses),
+    })
+}