// 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use rustc::hir::def_id::DefId; use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc::traits::{Clause, DomainGoal, Goal, PolyDomainGoal, ProgramClause, WhereClauseAtom}; use rustc::ty::subst::Substs; use rustc::ty::{self, Slice, TyCtxt}; use rustc_data_structures::sync::Lrc; use syntax::ast; use syntax::ast; use std::iter; trait Lower { /// Lower a rustc construction (e.g. `ty::TraitPredicate`) to a chalk-like type. fn lower(&self) -> T; } impl Lower> for Vec where T: Lower, { fn lower(&self) -> Vec { self.iter().map(|item| item.lower()).collect() } } impl<'tcx> Lower> for ty::TraitPredicate<'tcx> { fn lower(&self) -> WhereClauseAtom<'tcx> { WhereClauseAtom::Implemented(*self) } } impl<'tcx> Lower> for ty::ProjectionPredicate<'tcx> { fn lower(&self) -> WhereClauseAtom<'tcx> { WhereClauseAtom::ProjectionEq(*self) } } impl<'tcx, T> Lower> for T where T: Lower>, { fn lower(&self) -> DomainGoal<'tcx> { DomainGoal::Holds(self.lower()) } } impl<'tcx> Lower> for ty::RegionOutlivesPredicate<'tcx> { fn lower(&self) -> DomainGoal<'tcx> { DomainGoal::RegionOutlives(*self) } } impl<'tcx> Lower> for ty::TypeOutlivesPredicate<'tcx> { fn lower(&self) -> DomainGoal<'tcx> { DomainGoal::TypeOutlives(*self) } } /// `ty::Binder` is used for wrapping a rustc construction possibly containing generic /// lifetimes, e.g. `for<'a> T: Fn(&'a i32)`. Instead of representing higher-ranked things /// in that leaf-form (i.e. `Holds(Implemented(Binder))` in the previous /// example), we model them with quantified domain goals, e.g. as for the previous example: /// `forall<'a> { T: Fn(&'a i32) }` which corresponds to something like /// `Binder`. impl<'tcx, T> Lower> for ty::Binder where T: Lower> + ty::fold::TypeFoldable<'tcx>, { fn lower(&self) -> PolyDomainGoal<'tcx> { self.map_bound_ref(|p| p.lower()) } } impl<'tcx> Lower> for ty::Predicate<'tcx> { fn lower(&self) -> PolyDomainGoal<'tcx> { use rustc::ty::Predicate::*; match self { Trait(predicate) => predicate.lower(), RegionOutlives(predicate) => predicate.lower(), TypeOutlives(predicate) => predicate.lower(), Projection(predicate) => predicate.lower(), WellFormed(ty) => ty::Binder::dummy(DomainGoal::WellFormedTy(*ty)), ObjectSafe(..) | ClosureKind(..) | Subtype(..) | ConstEvaluatable(..) => { unimplemented!() } } } } /// Transforms an existing goal into a FromEnv goal. /// /// Used for lowered where clauses (see rustc guide). trait IntoFromEnvGoal { fn into_from_env_goal(self) -> Self; } impl<'tcx> IntoFromEnvGoal for DomainGoal<'tcx> { fn into_from_env_goal(self) -> DomainGoal<'tcx> { use self::DomainGoal::*; match self { Holds(wc_atom) => FromEnv(wc_atom), WellFormed(..) | FromEnv(..) | WellFormedTy(..) | FromEnvTy(..) | Normalize(..) | RegionOutlives(..) | TypeOutlives(..) => self, } } } crate fn program_clauses_for<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, ) -> Lrc<&'tcx Slice>> { let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); let node = tcx.hir.find(node_id).unwrap(); match node { hir::map::Node::NodeItem(item) => match item.node { hir::ItemTrait(..) => program_clauses_for_trait(tcx, def_id), hir::ItemImpl(..) => program_clauses_for_impl(tcx, def_id), _ => Lrc::new(tcx.mk_clauses(iter::empty::())), }, hir::map::Node::NodeImplItem(item) => { if let hir::ImplItemKind::Type(..) = item.node { program_clauses_for_associated_type_value(tcx, def_id) } else { Lrc::new(tcx.mk_clauses(iter::empty::())) } } // FIXME: other constructions e.g. traits, associated types... _ => Lrc::new(tcx.mk_clauses(iter::empty::())), } } fn program_clauses_for_trait<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, ) -> Lrc<&'tcx Slice>> { // `trait Trait where WC { .. } // P0 == Self` // Rule Implemented-From-Env (see rustc guide) // // ``` // forall { // Implemented(Self: Trait) :- FromEnv(Self: Trait) // } // ``` // `Self: Trait` let trait_pred = ty::TraitPredicate { trait_ref: ty::TraitRef { def_id, substs: Substs::identity_for_item(tcx, def_id), }, }; // `FromEnv(Self: Trait)` let from_env = Goal::from(DomainGoal::FromEnv(trait_pred.lower())); // `Implemented(Self: Trait)` let impl_trait = DomainGoal::Holds(WhereClauseAtom::Implemented(trait_pred)); // `Implemented(Self: Trait) :- FromEnv(Self: Trait)` let implemented_from_env = ProgramClause { goal: impl_trait, hypotheses: tcx.mk_goals(iter::once(from_env)), }; let clauses = iter::once(Clause::ForAll(ty::Binder::dummy(implemented_from_env))); // Rule Implied-Bound-From-Trait // // For each where clause WC: // ``` // forall { // FromEnv(WC) :- FromEnv(Self: Trait)`, for each where clause WC // FIXME: Remove the [1..] slice; this is a hack because the query // predicates_of currently includes the trait itself (`Self: Trait`). let where_clauses = &tcx.predicates_of(def_id).predicates; let implied_bound_clauses = where_clauses[1..] .into_iter() .map(|wc| implied_bound_from_trait(tcx, trait_pred, wc)); Lrc::new(tcx.mk_clauses(clauses.chain(implied_bound_clauses))) } /// For a given `where_clause`, returns a clause `FromEnv(WC) :- FromEnv(Self: Trait)`. fn implied_bound_from_trait<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_pred: ty::TraitPredicate<'tcx>, where_clause: &ty::Predicate<'tcx>, ) -> Clause<'tcx> { // `FromEnv(Self: Trait)` let impl_trait = DomainGoal::FromEnv(WhereClauseAtom::Implemented(trait_pred)); // `FromEnv(WC) :- FromEnv(Self: Trait)` Clause::ForAll(where_clause.lower().map_bound(|goal| ProgramClause { goal: goal.into_from_env_goal(), hypotheses: tcx.mk_goals(iter::once(Goal::from(impl_trait))), })) } fn program_clauses_for_impl<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, ) -> Lrc<&'tcx Slice>> { if let ImplPolarity::Negative = tcx.impl_polarity(def_id) { return Lrc::new(tcx.mk_clauses(iter::empty::())); } // Rule Implemented-From-Impl (see rustc guide) // // `impl Trait for A0 where WC { .. }` // // ``` // forall { // Implemented(A0: Trait) :- WC // } // ``` let trait_ref = tcx.impl_trait_ref(def_id).unwrap(); // `Implemented(A0: Trait)` let trait_pred = ty::TraitPredicate { trait_ref }.lower(); // `WC` let where_clauses = tcx.predicates_of(def_id).predicates.lower(); // `Implemented(A0: Trait) :- WC` let clause = ProgramClause { goal: trait_pred, hypotheses: tcx.mk_goals( where_clauses .into_iter() .map(|wc| Goal::from_poly_domain_goal(wc, tcx)), ), }; Lrc::new(tcx.mk_clauses(iter::once(Clause::ForAll(ty::Binder::dummy(clause))))) } pub fn program_clauses_for_associated_type_value<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: DefId, ) -> Lrc<&'tcx Slice>> { // Rule Normalize-From-Impl (see rustc guide) // // ```impl Trait for A0 // { // type AssocType where WC = T; // }``` // // ``` // forall { // forall { // Normalize(>::AssocType -> T) :- // Implemented(A0: Trait) && WC // } // } // ``` let item = tcx.associated_item(item_id); debug_assert_eq!(item.kind, ty::AssociatedKind::Type); let impl_id = if let ty::AssociatedItemContainer::ImplContainer(impl_id) = item.container { impl_id } else { bug!() }; // `A0 as Trait` let trait_ref = tcx.impl_trait_ref(impl_id).unwrap(); // `T` let ty = tcx.type_of(item_id); // `Implemented(A0: Trait)` let trait_implemented = ty::Binder::dummy(ty::TraitPredicate { trait_ref }.lower()); // `WC` let item_where_clauses = tcx.predicates_of(item_id).predicates.lower(); // `Implemented(A0: Trait) && WC` let mut where_clauses = vec![trait_implemented]; where_clauses.extend(item_where_clauses); // `>::AssocType` let projection_ty = ty::ProjectionTy::from_ref_and_name(tcx, trait_ref, item.name); // `Normalize(>::AssocType -> T)` let normalize_goal = DomainGoal::Normalize(ty::ProjectionPredicate { projection_ty, ty }); // `Normalize(... -> T) :- ...` let clause = ProgramClause { goal: normalize_goal, hypotheses: tcx.mk_goals( where_clauses .into_iter() .map(|wc| Goal::from_poly_domain_goal(wc, tcx)), ), }; Lrc::new(tcx.mk_clauses(iter::once(Clause::ForAll(ty::Binder::dummy(clause))))) } pub fn dump_program_clauses<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { if !tcx.features().rustc_attrs { return; } let mut visitor = ClauseDumper { tcx }; tcx.hir .krate() .visit_all_item_likes(&mut visitor.as_deep_visitor()); } struct ClauseDumper<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, } impl<'a, 'tcx> ClauseDumper<'a, 'tcx> { fn process_attrs(&mut self, node_id: ast::NodeId, attrs: &[ast::Attribute]) { let def_id = self.tcx.hir.local_def_id(node_id); for attr in attrs { if attr.check_name("rustc_dump_program_clauses") { let clauses = self.tcx.program_clauses_for(def_id); for clause in *clauses { // Skip the top-level binder for a less verbose output let program_clause = match clause { Clause::Implies(program_clause) => program_clause, Clause::ForAll(program_clause) => program_clause.skip_binder(), }; self.tcx .sess .struct_span_err(attr.span, &format!("{}", program_clause)) .emit(); } } } } } impl<'a, 'tcx> Visitor<'tcx> for ClauseDumper<'a, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { NestedVisitorMap::OnlyBodies(&self.tcx.hir) } fn visit_item(&mut self, item: &'tcx hir::Item) { self.process_attrs(item.id, &item.attrs); intravisit::walk_item(self, item); } fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { self.process_attrs(trait_item.id, &trait_item.attrs); intravisit::walk_trait_item(self, trait_item); } fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { self.process_attrs(impl_item.id, &impl_item.attrs); intravisit::walk_impl_item(self, impl_item); } fn visit_struct_field(&mut self, s: &'tcx hir::StructField) { self.process_attrs(s.id, &s.attrs); intravisit::walk_struct_field(self, s); } }