about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-10-19 06:38:10 +0000
committerbors <bors@rust-lang.org>2018-10-19 06:38:10 +0000
commitdbab381da1a46a18e46a04a61156aec40c59a4f6 (patch)
tree4fab9e0acd8a079380631377e31166166c570ce8
parentcb5e1b93e300cf9772a24c6de27d9f21cdae3123 (diff)
parent55ce7a266958aaed4774927ed1765576f561aa2d (diff)
downloadrust-dbab381da1a46a18e46a04a61156aec40c59a4f6.tar.gz
rust-dbab381da1a46a18e46a04a61156aec40c59a4f6.zip
Auto merge of #55040 - scalexm:param-env, r=nikomatsakis
Replace `ParamEnv` with a new type in chalk context.

I left a few FIXMEs.

r? @nikomatsakis
-rw-r--r--src/librustc/dep_graph/dep_node.rs4
-rw-r--r--src/librustc/ich/impls_ty.rs13
-rw-r--r--src/librustc/traits/mod.rs51
-rw-r--r--src/librustc/traits/structural_impls.rs47
-rw-r--r--src/librustc/ty/query/config.rs11
-rw-r--r--src/librustc/ty/query/keys.rs10
-rw-r--r--src/librustc/ty/query/mod.rs5
-rw-r--r--src/librustc/ty/query/plumbing.rs1
-rw-r--r--src/librustc_traits/chalk_context.rs87
-rw-r--r--src/librustc_traits/lowering/environment.rs256
-rw-r--r--src/librustc_traits/lowering/mod.rs (renamed from src/librustc_traits/lowering.rs)84
-rw-r--r--src/test/ui/chalkify/lower_env1.stderr3
-rw-r--r--src/test/ui/chalkify/lower_env2.rs26
-rw-r--r--src/test/ui/chalkify/lower_env2.stderr25
-rw-r--r--src/test/ui/chalkify/lower_env3.rs26
-rw-r--r--src/test/ui/chalkify/lower_env3.stderr20
16 files changed, 553 insertions, 116 deletions
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index 994bbe727fc..de03892b994 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -70,6 +70,7 @@ use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
 use std::fmt;
 use std::hash::Hash;
 use syntax_pos::symbol::InternedString;
+use traits;
 use traits::query::{
     CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal,
     CanonicalPredicateGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpNormalizeGoal,
@@ -550,6 +551,7 @@ define_dep_nodes!( <'tcx>
     [anon] TraitSelect,
 
     [] ParamEnv(DefId),
+    [] Environment(DefId),
     [] DescribeDef(DefId),
 
     // FIXME(mw): DefSpans are not really inputs since they are derived from
@@ -669,7 +671,7 @@ define_dep_nodes!( <'tcx>
     [input] Features,
 
     [] ProgramClausesFor(DefId),
-    [] ProgramClausesForEnv(ParamEnv<'tcx>),
+    [] ProgramClausesForEnv(traits::Environment<'tcx>),
     [] WasmImportModuleMap(CrateNum),
     [] ForeignModules(CrateNum),
 
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index e54968c5274..43448ad0d15 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -1395,10 +1395,16 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for traits::Goal<'tcx> {
 
 impl_stable_hash_for!(
     impl<'tcx> for struct traits::ProgramClause<'tcx> {
-        goal, hypotheses
+        goal, hypotheses, category
     }
 );
 
+impl_stable_hash_for!(enum traits::ProgramClauseCategory {
+    ImpliedBound,
+    WellFormed,
+    Other,
+});
+
 impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for traits::Clause<'tcx> {
     fn hash_stable<W: StableHasherResult>(&self,
                                           hcx: &mut StableHashingContext<'a>,
@@ -1422,3 +1428,8 @@ impl_stable_hash_for!(struct ty::subst::UserSubsts<'tcx> { substs, user_self_ty
 
 impl_stable_hash_for!(struct ty::subst::UserSelfTy<'tcx> { impl_def_id, self_ty });
 
+impl_stable_hash_for!(
+    impl<'tcx> for struct traits::Environment<'tcx> {
+        clauses,
+    }
+);
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 6e4abee32c0..d129cd486cf 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -278,6 +278,8 @@ pub type TraitObligations<'tcx> = Vec<TraitObligation<'tcx>>;
 /// * `DomainGoal`
 /// * `Goal`
 /// * `Clause`
+/// * `Environment`
+/// * `InEnvironment`
 /// are used for representing the trait system in the form of
 /// logic programming clauses. They are part of the interface
 /// for the chalk SLG solver.
@@ -335,6 +337,14 @@ impl<'tcx> DomainGoal<'tcx> {
     pub fn into_goal(self) -> GoalKind<'tcx> {
         GoalKind::DomainGoal(self)
     }
+
+    pub fn into_program_clause(self) -> ProgramClause<'tcx> {
+        ProgramClause {
+            goal: self,
+            hypotheses: ty::List::empty(),
+            category: ProgramClauseCategory::Other,
+        }
+    }
 }
 
 impl<'tcx> GoalKind<'tcx> {
@@ -360,6 +370,15 @@ pub enum Clause<'tcx> {
     ForAll(ty::Binder<ProgramClause<'tcx>>),
 }
 
+impl Clause<'tcx> {
+    pub fn category(self) -> ProgramClauseCategory {
+        match self {
+            Clause::Implies(clause) => clause.category,
+            Clause::ForAll(clause) => clause.skip_binder().category,
+        }
+    }
+}
+
 /// Multiple clauses.
 pub type Clauses<'tcx> = &'tcx List<Clause<'tcx>>;
 
@@ -376,6 +395,38 @@ pub struct ProgramClause<'tcx> {
 
     /// ...if we can prove these hypotheses (there may be no hypotheses at all):
     pub hypotheses: Goals<'tcx>,
+
+    /// Useful for filtering clauses.
+    pub category: ProgramClauseCategory,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum ProgramClauseCategory {
+    ImpliedBound,
+    WellFormed,
+    Other,
+}
+
+/// A set of clauses that we assume to be true.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct Environment<'tcx> {
+    pub clauses: Clauses<'tcx>,
+}
+
+impl Environment<'tcx> {
+    pub fn with<G>(self, goal: G) -> InEnvironment<'tcx, G> {
+        InEnvironment {
+            environment: self,
+            goal,
+        }
+    }
+}
+
+/// Something (usually a goal), along with an environment.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct InEnvironment<'tcx, G> {
+    pub environment: Environment<'tcx>,
+    pub goal: G,
 }
 
 pub type Selection<'tcx> = Vtable<'tcx, PredicateObligation<'tcx>>;
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs
index 1524f89af29..c50c9703eb5 100644
--- a/src/librustc/traits/structural_impls.rs
+++ b/src/librustc/traits/structural_impls.rs
@@ -496,7 +496,7 @@ impl<'tcx> fmt::Display for traits::Goal<'tcx> {
 
 impl<'tcx> fmt::Display for traits::ProgramClause<'tcx> {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let traits::ProgramClause { goal, hypotheses } = self;
+        let traits::ProgramClause { goal, hypotheses, .. } = self;
         write!(fmt, "{}", goal)?;
         if !hypotheses.is_empty() {
             write!(fmt, " :- ")?;
@@ -647,10 +647,15 @@ impl<'tcx> TypeFoldable<'tcx> for traits::Goal<'tcx> {
 BraceStructTypeFoldableImpl! {
     impl<'tcx> TypeFoldable<'tcx> for traits::ProgramClause<'tcx> {
         goal,
-        hypotheses
+        hypotheses,
+        category,
     }
 }
 
+CloneTypeFoldableAndLiftImpls! {
+    traits::ProgramClauseCategory,
+}
+
 EnumTypeFoldableImpl! {
     impl<'tcx> TypeFoldable<'tcx> for traits::Clause<'tcx> {
         (traits::Clause::Implies)(clause),
@@ -658,7 +663,43 @@ EnumTypeFoldableImpl! {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<traits::Clause<'tcx>> {
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for traits::Environment<'tcx> { clauses }
+}
+
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, G> TypeFoldable<'tcx> for traits::InEnvironment<'tcx, G> {
+        environment,
+        goal
+    } where G: TypeFoldable<'tcx>
+}
+
+impl<'a, 'tcx> Lift<'tcx> for traits::Environment<'a> {
+    type Lifted = traits::Environment<'tcx>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        tcx.lift(&self.clauses).map(|clauses| {
+            traits::Environment {
+                clauses,
+            }
+        })
+    }
+}
+
+impl<'a, 'tcx, G: Lift<'tcx>> Lift<'tcx> for traits::InEnvironment<'a, G> {
+    type Lifted = traits::InEnvironment<'tcx, G::Lifted>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        tcx.lift(&self.environment).and_then(|environment| {
+            tcx.lift(&self.goal).map(|goal| {
+                traits::InEnvironment {
+                    environment,
+                    goal,
+                }
+            })
+        })
+    }
+}
+
+impl<'tcx> TypeFoldable<'tcx> for traits::Clauses<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         let v = self.iter()
             .map(|t| t.fold_with(folder))
diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs
index 79eab3c6f34..d0c3109da52 100644
--- a/src/librustc/ty/query/config.rs
+++ b/src/librustc/ty/query/config.rs
@@ -12,6 +12,7 @@ use dep_graph::SerializedDepNodeIndex;
 use dep_graph::DepNode;
 use hir::def_id::{CrateNum, DefId, DefIndex};
 use mir::interpret::GlobalId;
+use traits;
 use traits::query::{
     CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal,
     CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal,
@@ -826,8 +827,14 @@ impl<'tcx> QueryDescription<'tcx> for queries::program_clauses_for<'tcx> {
 }
 
 impl<'tcx> QueryDescription<'tcx> for queries::program_clauses_for_env<'tcx> {
-    fn describe(_tcx: TyCtxt<'_, '_, '_>, _: ty::ParamEnv<'tcx>) -> Cow<'static, str> {
-        "generating chalk-style clauses for param env".into()
+    fn describe(_tcx: TyCtxt<'_, '_, '_>, _: traits::Environment<'tcx>) -> Cow<'static, str> {
+        "generating chalk-style clauses for environment".into()
+    }
+}
+
+impl<'tcx> QueryDescription<'tcx> for queries::environment<'tcx> {
+    fn describe(_tcx: TyCtxt<'_, '_, '_>, _: DefId) -> Cow<'static, str> {
+        "return a chalk-style environment".into()
     }
 }
 
diff --git a/src/librustc/ty/query/keys.rs b/src/librustc/ty/query/keys.rs
index 96b0a768001..f2d7a6792b5 100644
--- a/src/librustc/ty/query/keys.rs
+++ b/src/librustc/ty/query/keys.rs
@@ -12,6 +12,7 @@
 
 use infer::canonical::Canonical;
 use hir::def_id::{CrateNum, DefId, LOCAL_CRATE, DefIndex};
+use traits;
 use ty::{self, Ty, TyCtxt};
 use ty::subst::Substs;
 use ty::fast_reject::SimplifiedType;
@@ -181,6 +182,15 @@ impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> {
     }
 }
 
+impl<'tcx> Key for traits::Environment<'tcx> {
+    fn query_crate(&self) -> CrateNum {
+        LOCAL_CRATE
+    }
+    fn default_span(&self, _: TyCtxt<'_, '_, '_>) -> Span {
+        DUMMY_SP
+    }
+}
+
 impl Key for InternedString {
     fn query_crate(&self) -> CrateNum {
         LOCAL_CRATE
diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs
index 14e4ddfcdd6..e4fc45f3798 100644
--- a/src/librustc/ty/query/mod.rs
+++ b/src/librustc/ty/query/mod.rs
@@ -664,8 +664,11 @@ define_queries! { <'tcx>
         [] fn program_clauses_for: ProgramClausesFor(DefId) -> Clauses<'tcx>,
 
         [] fn program_clauses_for_env: ProgramClausesForEnv(
-            ty::ParamEnv<'tcx>
+            traits::Environment<'tcx>
         ) -> Clauses<'tcx>,
+
+        // Get the chalk-style environment of the given item.
+        [] fn environment: Environment(DefId) -> traits::Environment<'tcx>,
     },
 
     Linking {
diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs
index 39a59cf090e..f83f8bcf1a1 100644
--- a/src/librustc/ty/query/plumbing.rs
+++ b/src/librustc/ty/query/plumbing.rs
@@ -1156,6 +1156,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
         DepKind::CheckMatch => { force!(check_match, def_id!()); }
 
         DepKind::ParamEnv => { force!(param_env, def_id!()); }
+        DepKind::Environment => { force!(environment, def_id!()); }
         DepKind::DescribeDef => { force!(describe_def, def_id!()); }
         DepKind::DefSpan => { force!(def_span, def_id!()); }
         DepKind::LookupStability => { force!(lookup_stability, def_id!()); }
diff --git a/src/librustc_traits/chalk_context.rs b/src/librustc_traits/chalk_context.rs
index 536c1523406..371fa46f370 100644
--- a/src/librustc_traits/chalk_context.rs
+++ b/src/librustc_traits/chalk_context.rs
@@ -22,8 +22,10 @@ use rustc::traits::{
     ExClauseLift,
     Goal,
     GoalKind,
-    ProgramClause,
-    QuantifierKind
+    Clause,
+    QuantifierKind,
+    Environment,
+    InEnvironment,
 };
 use rustc::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 use rustc::ty::subst::Kind;
@@ -68,10 +70,10 @@ BraceStructTypeFoldableImpl! {
 impl context::Context for ChalkArenas<'tcx> {
     type CanonicalExClause = Canonical<'tcx, ExClause<Self>>;
 
-    type CanonicalGoalInEnvironment = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Goal<'tcx>>>;
+    type CanonicalGoalInEnvironment = Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>;
 
     // u-canonicalization not yet implemented
-    type UCanonicalGoalInEnvironment = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Goal<'tcx>>>;
+    type UCanonicalGoalInEnvironment = Canonical<'tcx, InEnvironment<'tcx, Goal<'tcx>>>;
 
     type CanonicalConstrainedSubst = Canonical<'tcx, ConstrainedSubst<'tcx>>;
 
@@ -82,13 +84,13 @@ impl context::Context for ChalkArenas<'tcx> {
 
     type InferenceNormalizedSubst = CanonicalVarValues<'tcx>;
 
-    type GoalInEnvironment = ty::ParamEnvAnd<'tcx, Goal<'tcx>>;
+    type GoalInEnvironment = InEnvironment<'tcx, Goal<'tcx>>;
 
     type RegionConstraint = QueryRegionConstraint<'tcx>;
 
     type Substitution = CanonicalVarValues<'tcx>;
 
-    type Environment = ty::ParamEnv<'tcx>;
+    type Environment = Environment<'tcx>;
 
     type Goal = Goal<'tcx>;
 
@@ -98,24 +100,24 @@ impl context::Context for ChalkArenas<'tcx> {
 
     type Parameter = Kind<'tcx>;
 
-    type ProgramClause = ProgramClause<'tcx>;
+    type ProgramClause = Clause<'tcx>;
 
-    type ProgramClauses = Vec<ProgramClause<'tcx>>;
+    type ProgramClauses = Vec<Clause<'tcx>>;
 
     type UnificationResult = InferOk<'tcx, ()>;
 
     fn goal_in_environment(
-        env: &ty::ParamEnv<'tcx>,
+        env: &Environment<'tcx>,
         goal: Goal<'tcx>,
-    ) -> ty::ParamEnvAnd<'tcx, Goal<'tcx>> {
-        env.and(goal)
+    ) -> InEnvironment<'tcx, Goal<'tcx>> {
+        env.with(goal)
     }
 }
 
 impl context::AggregateOps<ChalkArenas<'gcx>> for ChalkContext<'cx, 'gcx> {
     fn make_solution(
         &self,
-        _root_goal: &Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>>,
+        _root_goal: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
         _simplified_answers: impl context::AnswerStream<ChalkArenas<'gcx>>,
     ) -> Option<Canonical<'gcx, QueryResponse<'gcx, ()>>> {
         unimplemented!()
@@ -124,7 +126,10 @@ impl context::AggregateOps<ChalkArenas<'gcx>> for ChalkContext<'cx, 'gcx> {
 
 impl context::ContextOps<ChalkArenas<'gcx>> for ChalkContext<'cx, 'gcx> {
     /// True if this is a coinductive goal -- e.g., proving an auto trait.
-    fn is_coinductive(&self, _goal: &Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>>) -> bool {
+    fn is_coinductive(
+        &self,
+        _goal: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>
+    ) -> bool {
         unimplemented!()
     }
 
@@ -142,7 +147,7 @@ impl context::ContextOps<ChalkArenas<'gcx>> for ChalkContext<'cx, 'gcx> {
     /// - the environment and goal found by substitution `S` into `arg`
     fn instantiate_ucanonical_goal<R>(
         &self,
-        _arg: &Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>>,
+        _arg: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
         _op: impl context::WithInstantiatedUCanonicalGoal<ChalkArenas<'gcx>, Output = R>,
     ) -> R {
         unimplemented!()
@@ -175,19 +180,19 @@ impl context::ContextOps<ChalkArenas<'gcx>> for ChalkContext<'cx, 'gcx> {
     }
 
     fn canonical(
-        u_canon: &'a Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>>,
-    ) -> &'a Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>> {
+        u_canon: &'a Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
+    ) -> &'a Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>> {
         u_canon
     }
 
     fn is_trivial_substitution(
-        _u_canon: &Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>>,
+        _u_canon: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
         _canonical_subst: &Canonical<'gcx, ConstrainedSubst<'gcx>>,
     ) -> bool {
         unimplemented!()
     }
 
-    fn num_universes(_: &Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>>) -> usize {
+    fn num_universes(_: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>) -> usize {
         0 // FIXME
     }
 
@@ -196,8 +201,8 @@ impl context::ContextOps<ChalkArenas<'gcx>> for ChalkContext<'cx, 'gcx> {
     /// but for the universes of universally quantified names.
     fn map_goal_from_canonical(
         _map: &UniverseMap,
-        value: &Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>>,
-    ) -> Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>> {
+        value: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
+    ) -> Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>> {
         *value // FIXME universe maps not implemented yet
     }
 
@@ -267,10 +272,14 @@ impl context::InferenceTable<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
 
     fn add_clauses(
         &mut self,
-        _env: &ty::ParamEnv<'tcx>,
-        _clauses: Vec<ProgramClause<'tcx>>,
-    ) -> ty::ParamEnv<'tcx> {
-        panic!("FIXME no method to add clauses to ParamEnv yet")
+        env: &Environment<'tcx>,
+        clauses: Vec<Clause<'tcx>>,
+    ) -> Environment<'tcx> {
+        Environment {
+            clauses: self.infcx.tcx.mk_clauses(
+                env.clauses.iter().cloned().chain(clauses.into_iter())
+            )
+        }
     }
 }
 
@@ -279,10 +288,10 @@ impl context::ResolventOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
 {
     fn resolvent_clause(
         &mut self,
-        _environment: &ty::ParamEnv<'tcx>,
+        _environment: &Environment<'tcx>,
         _goal: &DomainGoal<'tcx>,
         _subst: &CanonicalVarValues<'tcx>,
-        _clause: &ProgramClause<'tcx>,
+        _clause: &Clause<'tcx>,
     ) -> chalk_engine::fallible::Fallible<Canonical<'gcx, ChalkExClause<'gcx>>> {
         panic!()
     }
@@ -290,8 +299,8 @@ impl context::ResolventOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
     fn apply_answer_subst(
         &mut self,
         _ex_clause: ChalkExClause<'tcx>,
-        _selected_goal: &ty::ParamEnvAnd<'tcx, Goal<'tcx>>,
-        _answer_table_goal: &Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>>,
+        _selected_goal: &InEnvironment<'tcx, Goal<'tcx>>,
+        _answer_table_goal: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
         _canonical_answer_subst: &Canonical<'gcx, ConstrainedSubst<'gcx>>,
     ) -> chalk_engine::fallible::Fallible<ChalkExClause<'tcx>> {
         panic!()
@@ -303,8 +312,8 @@ impl context::TruncateOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
 {
     fn truncate_goal(
         &mut self,
-        subgoal: &ty::ParamEnvAnd<'tcx, Goal<'tcx>>,
-    ) -> Option<ty::ParamEnvAnd<'tcx, Goal<'tcx>>> {
+        subgoal: &InEnvironment<'tcx, Goal<'tcx>>,
+    ) -> Option<InEnvironment<'tcx, Goal<'tcx>>> {
         Some(*subgoal) // FIXME we should truncate at some point!
     }
 
@@ -321,9 +330,9 @@ impl context::UnificationOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
 {
     fn program_clauses(
         &self,
-        _environment: &ty::ParamEnv<'tcx>,
+        _environment: &Environment<'tcx>,
         goal: &DomainGoal<'tcx>,
-    ) -> Vec<ProgramClause<'tcx>> {
+    ) -> Vec<Clause<'tcx>> {
         use rustc::traits::WhereClause::*;
 
         match goal {
@@ -389,8 +398,8 @@ impl context::UnificationOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
 
     fn canonicalize_goal(
         &mut self,
-        value: &ty::ParamEnvAnd<'tcx, Goal<'tcx>>,
-    ) -> Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>> {
+        value: &InEnvironment<'tcx, Goal<'tcx>>,
+    ) -> Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>> {
         let mut _orig_values = OriginalQueryValues::default();
         self.infcx.canonicalize_query(value, &mut _orig_values)
     }
@@ -412,9 +421,9 @@ impl context::UnificationOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
 
     fn u_canonicalize_goal(
         &mut self,
-        value: &Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>>,
+        value: &Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
     ) -> (
-        Canonical<'gcx, ty::ParamEnvAnd<'gcx, Goal<'gcx>>>,
+        Canonical<'gcx, InEnvironment<'gcx, Goal<'gcx>>>,
         UniverseMap,
     ) {
         (value.clone(), UniverseMap)
@@ -422,14 +431,14 @@ impl context::UnificationOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
 
     fn invert_goal(
         &mut self,
-        _value: &ty::ParamEnvAnd<'tcx, Goal<'tcx>>,
-    ) -> Option<ty::ParamEnvAnd<'tcx, Goal<'tcx>>> {
+        _value: &InEnvironment<'tcx, Goal<'tcx>>,
+    ) -> Option<InEnvironment<'tcx, Goal<'tcx>>> {
         panic!("goal inversion not yet implemented")
     }
 
     fn unify_parameters(
         &mut self,
-        _environment: &ty::ParamEnv<'tcx>,
+        _environment: &Environment<'tcx>,
         _a: &Kind<'tcx>,
         _b: &Kind<'tcx>,
     ) -> ChalkEngineFallible<InferOk<'tcx, ()>> {
diff --git a/src/librustc_traits/lowering/environment.rs b/src/librustc_traits/lowering/environment.rs
new file mode 100644
index 00000000000..3d1e7cf17a6
--- /dev/null
+++ b/src/librustc_traits/lowering/environment.rs
@@ -0,0 +1,256 @@
+// 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;
+
+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(_region, _sub_ty, ..) => {
+                // FIXME: we'd need bound tys in order to properly write the above rule
+            }
+
+            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::Never |
+            ty::Tuple(..) |
+            ty::GeneratorWitness(..) |
+            ty::UnnormalizedProjection(..) |
+            ty::Param(..) |
+            ty::Infer(..) |
+            ty::Error => (),
+        }
+    }
+
+    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();
+    {
+        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();
+    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) -> Environment<'tcx> {
+    use super::{Lower, IntoFromEnvGoal};
+    use rustc::hir::{Node, TraitItemKind, ImplItemKind, ItemKind, ForeignItemKind};
+
+    // 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(|predicate| predicate.lower())
+        .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 receiver type and all its constituents
+    // are well-formed.
+    if is_impl {
+        let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl");
+        input_tys.extend(trait_ref.self_ty().walk());
+    }
+
+    // In an fn, we assume that the arguments and all their constituents are
+    // well-formed.
+    if is_fn {
+        let fn_sig = tcx.fn_sig(def_id);
+        input_tys.extend(
+            // FIXME: `skip_binder` seems ok for now? In a real setting,
+            // the late bound regions would next be instantiated with things
+            // in the inference table.
+            fn_sig.skip_binder().inputs().iter().flat_map(|ty| ty.walk())
+        );
+    }
+
+    let clauses = clauses.chain(
+        input_tys.into_iter()
+            .map(|ty| DomainGoal::FromEnv(FromEnv::Ty(ty)))
+            .map(|domain_goal| domain_goal.into_program_clause())
+            .map(Clause::Implies)
+    );
+
+    Environment {
+        clauses: tcx.mk_clauses(clauses),
+    }
+}
diff --git a/src/librustc_traits/lowering.rs b/src/librustc_traits/lowering/mod.rs
index 1e3f0a21cef..fb598a33548 100644
--- a/src/librustc_traits/lowering.rs
+++ b/src/librustc_traits/lowering/mod.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+mod environment;
+
 use rustc::hir::def_id::DefId;
 use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc::hir::map::definitions::DefPathData;
@@ -20,13 +22,12 @@ use rustc::traits::{
     GoalKind,
     PolyDomainGoal,
     ProgramClause,
+    ProgramClauseCategory,
     WellFormed,
     WhereClause,
 };
 use rustc::ty::query::Providers;
 use rustc::ty::{self, List, TyCtxt};
-use rustc_data_structures::fx::FxHashSet;
-use std::mem;
 use syntax::ast;
 
 use std::iter;
@@ -34,7 +35,8 @@ use std::iter;
 crate fn provide(p: &mut Providers) {
     *p = Providers {
         program_clauses_for,
-        program_clauses_for_env,
+        program_clauses_for_env: environment::program_clauses_for_env,
+        environment: environment::environment,
         ..*p
     };
 }
@@ -173,66 +175,6 @@ crate fn program_clauses_for<'a, 'tcx>(
     }
 }
 
-crate fn program_clauses_for_env<'a, 'tcx>(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-) -> Clauses<'tcx> {
-    debug!("program_clauses_for_env(param_env={:?})", param_env);
-
-    let mut last_round = FxHashSet();
-    last_round.extend(
-        param_env
-            .caller_bounds
-            .iter()
-            .flat_map(|&p| predicate_def_id(p)),
-    );
-
-    let mut closure = last_round.clone();
-    let mut next_round = FxHashSet();
-    while !last_round.is_empty() {
-        next_round.extend(
-            last_round
-                .drain()
-                .flat_map(|def_id| {
-                    tcx.predicates_of(def_id)
-                        .instantiate_identity(tcx)
-                        .predicates
-                })
-                .flat_map(|p| predicate_def_id(p))
-                .filter(|&def_id| closure.insert(def_id)),
-        );
-        mem::swap(&mut next_round, &mut last_round);
-    }
-
-    debug!("program_clauses_for_env: closure = {:#?}", closure);
-
-    return tcx.mk_clauses(
-        closure
-            .into_iter()
-            .flat_map(|def_id| tcx.program_clauses_for(def_id).iter().cloned()),
-    );
-
-    /// Given that `predicate` is in the environment, returns the
-    /// def-id of something (e.g., a trait, associated item, etc)
-    /// whose predicates can also be assumed to be true. We will
-    /// compute the transitive closure of such things.
-    fn predicate_def_id<'tcx>(predicate: ty::Predicate<'tcx>) -> Option<DefId> {
-        match predicate {
-            ty::Predicate::Trait(predicate) => Some(predicate.def_id()),
-
-            ty::Predicate::Projection(projection) => Some(projection.item_def_id()),
-
-            ty::Predicate::WellFormed(..)
-            | ty::Predicate::RegionOutlives(..)
-            | ty::Predicate::TypeOutlives(..)
-            | ty::Predicate::ObjectSafe(..)
-            | ty::Predicate::ClosureKind(..)
-            | ty::Predicate::Subtype(..)
-            | ty::Predicate::ConstEvaluatable(..) => None,
-        }
-    }
-}
-
 fn program_clauses_for_trait<'a, 'tcx>(
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     def_id: DefId,
@@ -263,6 +205,7 @@ fn program_clauses_for_trait<'a, 'tcx>(
     let implemented_from_env = ProgramClause {
         goal: impl_trait,
         hypotheses,
+        category: ProgramClauseCategory::ImpliedBound,
     };
 
     let clauses = iter::once(Clause::ForAll(ty::Binder::dummy(implemented_from_env)));
@@ -290,6 +233,7 @@ fn program_clauses_for_trait<'a, 'tcx>(
         .map(|wc| wc.map_bound(|goal| ProgramClause {
             goal: goal.into_from_env_goal(),
             hypotheses,
+            category: ProgramClauseCategory::ImpliedBound,
         }))
         .map(Clause::ForAll);
 
@@ -316,6 +260,7 @@ fn program_clauses_for_trait<'a, 'tcx>(
         hypotheses: tcx.mk_goals(
             wf_conditions.map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))),
         ),
+        category: ProgramClauseCategory::WellFormed,
     };
     let wf_clause = iter::once(Clause::ForAll(ty::Binder::dummy(wf_clause)));
 
@@ -358,6 +303,7 @@ fn program_clauses_for_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId
             where_clauses
                 .map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))),
         ),
+        category: ProgramClauseCategory::Other,
     };
     tcx.mk_clauses(iter::once(Clause::ForAll(ty::Binder::dummy(clause))))
 }
@@ -394,6 +340,7 @@ pub fn program_clauses_for_type_def<'a, 'tcx>(
                 .cloned()
                 .map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))),
         ),
+        category: ProgramClauseCategory::WellFormed,
     };
 
     let well_formed_clause = iter::once(Clause::ForAll(ty::Binder::dummy(well_formed)));
@@ -419,6 +366,7 @@ pub fn program_clauses_for_type_def<'a, 'tcx>(
         .map(|wc| wc.map_bound(|goal| ProgramClause {
             goal: goal.into_from_env_goal(),
             hypotheses,
+            category: ProgramClauseCategory::ImpliedBound,
         }))
 
         .map(Clause::ForAll);
@@ -466,7 +414,8 @@ pub fn program_clauses_for_associated_type_def<'a, 'tcx>(
 
     let projection_eq_clause = ProgramClause {
         goal: DomainGoal::Holds(projection_eq),
-        hypotheses: &ty::List::empty(),
+        hypotheses: ty::List::empty(),
+        category: ProgramClauseCategory::Other,
     };
 
     // Rule WellFormed-AssocTy
@@ -484,6 +433,7 @@ pub fn program_clauses_for_associated_type_def<'a, 'tcx>(
     let wf_clause = ProgramClause {
         goal: DomainGoal::WellFormed(WellFormed::Ty(placeholder_ty)),
         hypotheses: tcx.mk_goals(iter::once(hypothesis)),
+        category: ProgramClauseCategory::Other,
     };
 
     // Rule Implied-Trait-From-AssocTy
@@ -500,6 +450,7 @@ pub fn program_clauses_for_associated_type_def<'a, 'tcx>(
     let from_env_clause = ProgramClause {
         goal: DomainGoal::FromEnv(FromEnv::Trait(trait_predicate)),
         hypotheses: tcx.mk_goals(iter::once(hypothesis)),
+        category: ProgramClauseCategory::ImpliedBound,
     };
 
     let clauses = iter::once(projection_eq_clause)
@@ -565,6 +516,7 @@ pub fn program_clauses_for_associated_type_value<'a, 'tcx>(
                 .into_iter()
                 .map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))),
         ),
+        category: ProgramClauseCategory::Other,
     };
     tcx.mk_clauses(iter::once(Clause::ForAll(ty::Binder::dummy(clause))))
 }
@@ -595,8 +547,8 @@ impl<'a, 'tcx> ClauseDumper<'a, 'tcx> {
             }
 
             if attr.check_name("rustc_dump_env_program_clauses") {
-                let param_env = self.tcx.param_env(def_id);
-                clauses = Some(self.tcx.program_clauses_for_env(param_env));
+                let environment = self.tcx.environment(def_id);
+                clauses = Some(self.tcx.program_clauses_for_env(environment));
             }
 
             if let Some(clauses) = clauses {
diff --git a/src/test/ui/chalkify/lower_env1.stderr b/src/test/ui/chalkify/lower_env1.stderr
index 4a3e14ac034..3aa04cfeb67 100644
--- a/src/test/ui/chalkify/lower_env1.stderr
+++ b/src/test/ui/chalkify/lower_env1.stderr
@@ -18,9 +18,6 @@ LL | #[rustc_dump_env_program_clauses] //~ ERROR program clause dump
    = note: Implemented(Self: Bar) :- FromEnv(Self: Bar).
    = note: Implemented(Self: Foo) :- FromEnv(Self: Foo).
    = note: Implemented(Self: std::marker::Sized) :- FromEnv(Self: std::marker::Sized).
-   = note: WellFormed(Self: Bar) :- Implemented(Self: Bar), WellFormed(Self: Foo).
-   = note: WellFormed(Self: Foo) :- Implemented(Self: Foo).
-   = note: WellFormed(Self: std::marker::Sized) :- Implemented(Self: std::marker::Sized).
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/chalkify/lower_env2.rs b/src/test/ui/chalkify/lower_env2.rs
new file mode 100644
index 00000000000..0b50dbfdf95
--- /dev/null
+++ b/src/test/ui/chalkify/lower_env2.rs
@@ -0,0 +1,26 @@
+// 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.
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+trait Foo { }
+
+#[rustc_dump_program_clauses] //~ ERROR program clause dump
+struct S<'a, T> where T: Foo {
+    data: &'a T,
+}
+
+#[rustc_dump_env_program_clauses] //~ ERROR program clause dump
+fn bar<'a, T: Foo>(x: S<T>) {
+}
+
+fn main() {
+}
diff --git a/src/test/ui/chalkify/lower_env2.stderr b/src/test/ui/chalkify/lower_env2.stderr
new file mode 100644
index 00000000000..3b88ac1f22b
--- /dev/null
+++ b/src/test/ui/chalkify/lower_env2.stderr
@@ -0,0 +1,25 @@
+error: program clause dump
+  --> $DIR/lower_env2.rs:16:1
+   |
+LL | #[rustc_dump_program_clauses] //~ ERROR program clause dump
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: FromEnv(T: Foo) :- FromEnv(S<'a, T>).
+   = note: FromEnv(T: std::marker::Sized) :- FromEnv(S<'a, T>).
+   = note: TypeOutlives(T : 'a) :- FromEnv(S<'a, T>).
+   = note: WellFormed(S<'a, T>) :- Implemented(T: std::marker::Sized), Implemented(T: Foo), TypeOutlives(T : 'a).
+
+error: program clause dump
+  --> $DIR/lower_env2.rs:21:1
+   |
+LL | #[rustc_dump_env_program_clauses] //~ ERROR program clause dump
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: FromEnv(T: Foo) :- FromEnv(S<'a, T>).
+   = note: FromEnv(T: std::marker::Sized) :- FromEnv(S<'a, T>).
+   = note: Implemented(Self: Foo) :- FromEnv(Self: Foo).
+   = note: Implemented(Self: std::marker::Sized) :- FromEnv(Self: std::marker::Sized).
+   = note: TypeOutlives(T : 'a) :- FromEnv(S<'a, T>).
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/chalkify/lower_env3.rs b/src/test/ui/chalkify/lower_env3.rs
new file mode 100644
index 00000000000..1f8bc49e309
--- /dev/null
+++ b/src/test/ui/chalkify/lower_env3.rs
@@ -0,0 +1,26 @@
+// 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.
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+trait Foo {
+    #[rustc_dump_env_program_clauses] //~ ERROR program clause dump
+    fn foo(&self);
+}
+
+impl<T> Foo for T where T: Clone {
+    #[rustc_dump_env_program_clauses] //~ ERROR program clause dump
+    fn foo(&self) {
+    }
+}
+
+fn main() {
+}
diff --git a/src/test/ui/chalkify/lower_env3.stderr b/src/test/ui/chalkify/lower_env3.stderr
new file mode 100644
index 00000000000..ac0f8e34cd4
--- /dev/null
+++ b/src/test/ui/chalkify/lower_env3.stderr
@@ -0,0 +1,20 @@
+error: program clause dump
+  --> $DIR/lower_env3.rs:15:5
+   |
+LL |     #[rustc_dump_env_program_clauses] //~ ERROR program clause dump
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: Implemented(Self: Foo) :- FromEnv(Self: Foo).
+
+error: program clause dump
+  --> $DIR/lower_env3.rs:20:5
+   |
+LL |     #[rustc_dump_env_program_clauses] //~ ERROR program clause dump
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: FromEnv(Self: std::marker::Sized) :- FromEnv(Self: std::clone::Clone).
+   = note: Implemented(Self: std::clone::Clone) :- FromEnv(Self: std::clone::Clone).
+   = note: Implemented(Self: std::marker::Sized) :- FromEnv(Self: std::marker::Sized).
+
+error: aborting due to 2 previous errors
+