From c3b33a7f3be706d35ee62847342c22406401430a Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 8 Feb 2019 10:46:53 +0100 Subject: [PATCH 1/9] Fix a bug in chalk unification code --- src/librustc/infer/nll_relate/mod.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index 7140af36acbdc..2db3208953eb0 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs @@ -239,6 +239,7 @@ where first_free_index: ty::DebruijnIndex, scopes: &[BoundRegionScope<'tcx>], ) -> ty::Region<'tcx> { + debug!("replace_bound_regions(scopes={:?})", scopes); if let ty::ReLateBound(debruijn, br) = r { Self::lookup_bound_region(*debruijn, br, first_free_index, scopes) } else { @@ -421,7 +422,13 @@ where // Forbid inference variables in the RHS. bug!("unexpected inference var {:?}", b) } else { - self.relate_ty_var(vid, a) + // We swap the order of `a` and `b` in the call to + // `relate_ty_var` below, so swap the corresponding scopes + // as well. + std::mem::swap(&mut self.a_scopes, &mut self.b_scopes); + let res = self.relate_ty_var(vid, a); + std::mem::swap(&mut self.a_scopes, &mut self.b_scopes); + res } } @@ -436,7 +443,12 @@ where (_, &ty::Projection(projection_ty)) if D::normalization() == NormalizationStrategy::Lazy => { - Ok(self.relate_projection_ty(projection_ty, a)) + // Swap the respective scopes of `a` and `b` (see comment + // above). + std::mem::swap(&mut self.a_scopes, &mut self.b_scopes); + let res = self.relate_projection_ty(projection_ty, a); + std::mem::swap(&mut self.a_scopes, &mut self.b_scopes); + Ok(res) } _ => { From 60ea7cbe4eebc04e8910c63baeabd5d385fadc4c Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 8 Feb 2019 11:21:03 +0100 Subject: [PATCH 2/9] Gather region constraints not coming from unification --- .../chalk_context/program_clauses.rs | 37 +++++++------------ .../chalk_context/resolvent_ops.rs | 24 ++++++++++-- src/librustc_traits/lowering/environment.rs | 28 +------------- src/test/ui/chalkify/lower_env3.stderr | 2 - 4 files changed, 34 insertions(+), 57 deletions(-) diff --git a/src/librustc_traits/chalk_context/program_clauses.rs b/src/librustc_traits/chalk_context/program_clauses.rs index 8d5d2b8a9a255..fb7bba32d395e 100644 --- a/src/librustc_traits/chalk_context/program_clauses.rs +++ b/src/librustc_traits/chalk_context/program_clauses.rs @@ -326,15 +326,12 @@ fn wf_clause_for_ref<'tcx>( mutbl, }); - let _outlives: DomainGoal<'_> = ty::OutlivesPredicate(ty, region).lower(); + let outlives: DomainGoal<'_> = ty::OutlivesPredicate(ty, region).lower(); let wf_clause = ProgramClause { goal: DomainGoal::WellFormed(WellFormed::Ty(ref_ty)), - hypotheses: ty::List::empty(), - - // FIXME: restore this later once we get better at handling regions - // hypotheses: tcx.mk_goals( - // iter::once(tcx.mk_goal(outlives.into_goal())) - // ), + hypotheses: tcx.mk_goals( + iter::once(tcx.mk_goal(outlives.into_goal())) + ), category: ProgramClauseCategory::WellFormed, }; let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); @@ -432,22 +429,14 @@ impl ChalkInferenceContext<'cx, 'gcx, 'tcx> { clauses } - DomainGoal::Holds(RegionOutlives(..)) => { - // These come from: - // * implied bounds from trait definitions (rule `Implied-Bound-From-Trait`) - // * implied bounds from type definitions (rule `Implied-Bound-From-Type`) - - // All of these rules are computed in the environment. - vec![] - } - - DomainGoal::Holds(TypeOutlives(..)) => { - // These come from: - // * implied bounds from trait definitions (rule `Implied-Bound-From-Trait`) - // * implied bounds from type definitions (rule `Implied-Bound-From-Type`) - - // All of these rules are computed in the environment. - vec![] + // For outlive requirements, just assume they hold. `ResolventOps::resolvent_clause` + // will register them as actual region constraints later. + DomainGoal::Holds(RegionOutlives(..)) | DomainGoal::Holds(TypeOutlives(..)) => { + vec![Clause::Implies(ProgramClause { + goal, + hypotheses: ty::List::empty(), + category: ProgramClauseCategory::Other, + })] } DomainGoal::WellFormed(WellFormed::Trait(trait_predicate)) => { @@ -485,7 +474,7 @@ impl ChalkInferenceContext<'cx, 'gcx, 'tcx> { ty::Error | ty::Never => { let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(ty)), + goal, hypotheses: ty::List::empty(), category: ProgramClauseCategory::WellFormed, }; diff --git a/src/librustc_traits/chalk_context/resolvent_ops.rs b/src/librustc_traits/chalk_context/resolvent_ops.rs index 932501cc04fe0..41f983e6acac8 100644 --- a/src/librustc_traits/chalk_context/resolvent_ops.rs +++ b/src/librustc_traits/chalk_context/resolvent_ops.rs @@ -8,6 +8,7 @@ use rustc::infer::{InferCtxt, LateBoundRegionConversionTime}; use rustc::infer::canonical::{Canonical, CanonicalVarValues}; use rustc::traits::{ DomainGoal, + WhereClause, Goal, GoalKind, Clause, @@ -75,6 +76,23 @@ impl context::ResolventOps, ChalkArenas<'tcx>> }) ); + // If we have a goal of the form `T: 'a` or `'a: 'b`, then just + // assume it is true (no subgoals) and register it as a constraint + // instead. + match goal { + DomainGoal::Holds(WhereClause::RegionOutlives(pred)) => { + assert_eq!(ex_clause.subgoals.len(), 0); + ex_clause.constraints.push(ty::OutlivesPredicate(pred.0.into(), pred.1)); + } + + DomainGoal::Holds(WhereClause::TypeOutlives(pred)) => { + assert_eq!(ex_clause.subgoals.len(), 0); + ex_clause.constraints.push(ty::OutlivesPredicate(pred.0.into(), pred.1)); + } + + _ => (), + }; + let canonical_ex_clause = self.canonicalize_ex_clause(&ex_clause); Ok(canonical_ex_clause) }); @@ -112,10 +130,8 @@ impl context::ResolventOps, ChalkArenas<'tcx>> substitutor.relate(&answer_table_goal.value, &selected_goal) .map_err(|_| NoSolution)?; - let ex_clause = substitutor.ex_clause; - - // FIXME: restore this later once we get better at handling regions - // ex_clause.constraints.extend(answer_subst.constraints); + let mut ex_clause = substitutor.ex_clause; + ex_clause.constraints.extend(answer_subst.constraints); debug!("apply_answer_subst: ex_clause = {:?}", ex_clause); Ok(ex_clause) diff --git a/src/librustc_traits/lowering/environment.rs b/src/librustc_traits/lowering/environment.rs index c908c6993e19e..3570cb1024600 100644 --- a/src/librustc_traits/lowering/environment.rs +++ b/src/librustc_traits/lowering/environment.rs @@ -10,9 +10,6 @@ use rustc::traits::{ use rustc::ty::{self, TyCtxt, Ty}; use rustc::hir::def_id::DefId; use rustc_data_structures::fx::FxHashSet; -use super::Lower; -use crate::generic_types; -use std::iter; struct ClauseVisitor<'set, 'a, 'tcx: 'a + 'set> { tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -38,30 +35,6 @@ impl ClauseVisitor<'set, 'a, 'tcx> { ); } - // forall<'a, T> { `Outlives(T: 'a) :- FromEnv(&'a T)` } - ty::Ref(_, _, mutbl) => { - let region = self.tcx.mk_region( - ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(0)) - ); - let ty = generic_types::bound(self.tcx, 1); - let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut { - ty, - mutbl, - }); - - 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 } @@ -99,6 +72,7 @@ impl ClauseVisitor<'set, 'a, 'tcx> { ty::RawPtr(..) | ty::FnPtr(..) | ty::Tuple(..) | + ty::Ref(..) | ty::Never | ty::Infer(..) | ty::Placeholder(..) | diff --git a/src/test/ui/chalkify/lower_env3.stderr b/src/test/ui/chalkify/lower_env3.stderr index 46e083686895d..a1fc83bfea8a3 100644 --- a/src/test/ui/chalkify/lower_env3.stderr +++ b/src/test/ui/chalkify/lower_env3.stderr @@ -4,7 +4,6 @@ error: program clause dump LL | #[rustc_dump_env_program_clauses] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: forall<'^0, ^1> { TypeOutlives(^1: '^0) :- FromEnv(&^1). } = note: forall { Implemented(Self: Foo) :- FromEnv(Self: Foo). } error: program clause dump @@ -13,7 +12,6 @@ error: program clause dump LL | #[rustc_dump_env_program_clauses] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: forall<'^0, ^1> { TypeOutlives(^1: '^0) :- FromEnv(&^1). } = note: forall { FromEnv(Self: std::marker::Sized) :- FromEnv(Self: std::clone::Clone). } = note: forall { Implemented(Self: std::clone::Clone) :- FromEnv(Self: std::clone::Clone). } = note: forall { Implemented(Self: std::marker::Sized) :- FromEnv(Self: std::marker::Sized). } From 66b4a0852d4726dc9b4836e50cf7e23662c47ae2 Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 8 Feb 2019 12:44:16 +0100 Subject: [PATCH 3/9] Reorganize `chalk_context::program_clauses` --- .../chalk_context/program_clauses/builtin.rs | 132 +++++++ .../mod.rs} | 326 +----------------- .../program_clauses/primitive.rs | 204 +++++++++++ 3 files changed, 343 insertions(+), 319 deletions(-) create mode 100644 src/librustc_traits/chalk_context/program_clauses/builtin.rs rename src/librustc_traits/chalk_context/{program_clauses.rs => program_clauses/mod.rs} (50%) create mode 100644 src/librustc_traits/chalk_context/program_clauses/primitive.rs diff --git a/src/librustc_traits/chalk_context/program_clauses/builtin.rs b/src/librustc_traits/chalk_context/program_clauses/builtin.rs new file mode 100644 index 0000000000000..3622cacbb012e --- /dev/null +++ b/src/librustc_traits/chalk_context/program_clauses/builtin.rs @@ -0,0 +1,132 @@ +use rustc::traits::{ + GoalKind, + Clause, + ProgramClause, + ProgramClauseCategory, +}; +use rustc::ty; +use rustc::ty::subst::{InternalSubsts, Subst}; +use rustc::hir::def_id::DefId; +use crate::lowering::Lower; +use crate::generic_types; + +crate fn assemble_builtin_sized_impls<'tcx>( + tcx: ty::TyCtxt<'_, '_, 'tcx>, + sized_def_id: DefId, + ty: ty::Ty<'tcx>, + clauses: &mut Vec> +) { + let mut push_builtin_impl = |ty: ty::Ty<'tcx>, nested: &[ty::Ty<'tcx>]| { + let clause = ProgramClause { + goal: ty::TraitPredicate { + trait_ref: ty::TraitRef { + def_id: sized_def_id, + substs: tcx.mk_substs_trait(ty, &[]), + }, + }.lower(), + hypotheses: tcx.mk_goals( + nested.iter() + .cloned() + .map(|nested_ty| ty::TraitRef { + def_id: sized_def_id, + substs: tcx.mk_substs_trait(nested_ty, &[]), + }) + .map(|trait_ref| ty::TraitPredicate { trait_ref }) + .map(|pred| GoalKind::DomainGoal(pred.lower())) + .map(|goal_kind| tcx.mk_goal(goal_kind)) + ), + category: ProgramClauseCategory::Other, + }; + // Bind innermost bound vars that may exist in `ty` and `nested`. + clauses.push(Clause::ForAll(ty::Binder::bind(clause))); + }; + + match &ty.sty { + // Non parametric primitive types. + ty::Bool | + ty::Char | + ty::Int(..) | + ty::Uint(..) | + ty::Float(..) | + ty::Error | + ty::Never => push_builtin_impl(ty, &[]), + + // These ones are always `Sized`. + &ty::Array(_, length) => { + push_builtin_impl(tcx.mk_ty(ty::Array(generic_types::bound(tcx, 0), length)), &[]); + } + ty::RawPtr(ptr) => { + push_builtin_impl(generic_types::raw_ptr(tcx, ptr.mutbl), &[]); + } + &ty::Ref(_, _, mutbl) => { + push_builtin_impl(generic_types::ref_ty(tcx, mutbl), &[]); + } + ty::FnPtr(fn_ptr) => { + let fn_ptr = fn_ptr.skip_binder(); + let fn_ptr = generic_types::fn_ptr( + tcx, + fn_ptr.inputs_and_output.len(), + fn_ptr.c_variadic, + fn_ptr.unsafety, + fn_ptr.abi + ); + push_builtin_impl(fn_ptr, &[]); + } + &ty::FnDef(def_id, ..) => { + push_builtin_impl(generic_types::fn_def(tcx, def_id), &[]); + } + &ty::Closure(def_id, ..) => { + push_builtin_impl(generic_types::closure(tcx, def_id), &[]); + } + &ty::Generator(def_id, ..) => { + push_builtin_impl(generic_types::generator(tcx, def_id), &[]); + } + + // `Sized` if the last type is `Sized` (because else we will get a WF error anyway). + &ty::Tuple(type_list) => { + let type_list = generic_types::type_list(tcx, type_list.len()); + push_builtin_impl(tcx.mk_ty(ty::Tuple(type_list)), &**type_list); + } + + // Struct def + ty::Adt(adt_def, _) => { + let substs = InternalSubsts::bound_vars_for_item(tcx, adt_def.did); + let adt = tcx.mk_ty(ty::Adt(adt_def, substs)); + let sized_constraint = adt_def.sized_constraint(tcx) + .iter() + .map(|ty| ty.subst(tcx, substs)) + .collect::>(); + push_builtin_impl(adt, &sized_constraint); + } + + // Artificially trigger an ambiguity. + ty::Infer(..) => { + // Everybody can find at least two types to unify against: + // general ty vars, int vars and float vars. + push_builtin_impl(tcx.types.i32, &[]); + push_builtin_impl(tcx.types.u32, &[]); + push_builtin_impl(tcx.types.f32, &[]); + push_builtin_impl(tcx.types.f64, &[]); + } + + ty::Projection(_projection_ty) => { + // FIXME: add builtin impls from the associated type values found in + // trait impls of `projection_ty.trait_ref(tcx)`. + } + + // The `Sized` bound can only come from the environment. + ty::Param(..) | + ty::Placeholder(..) | + ty::UnnormalizedProjection(..) => (), + + // Definitely not `Sized`. + ty::Foreign(..) | + ty::Str | + ty::Slice(..) | + ty::Dynamic(..) | + ty::Opaque(..) => (), + + ty::Bound(..) | + ty::GeneratorWitness(..) => bug!("unexpected type {:?}", ty), + } +} diff --git a/src/librustc_traits/chalk_context/program_clauses.rs b/src/librustc_traits/chalk_context/program_clauses/mod.rs similarity index 50% rename from src/librustc_traits/chalk_context/program_clauses.rs rename to src/librustc_traits/chalk_context/program_clauses/mod.rs index fb7bba32d395e..b6fb70b057779 100644 --- a/src/librustc_traits/chalk_context/program_clauses.rs +++ b/src/librustc_traits/chalk_context/program_clauses/mod.rs @@ -1,24 +1,23 @@ +mod builtin; +mod primitive; + use rustc::traits::{ WellFormed, FromEnv, DomainGoal, - GoalKind, Clause, - Clauses, ProgramClause, ProgramClauseCategory, Environment, }; use rustc::ty; -use rustc::ty::subst::{InternalSubsts, Subst}; -use rustc::hir; use rustc::hir::def_id::DefId; -use rustc_target::spec::abi; use super::ChalkInferenceContext; -use crate::lowering::Lower; -use crate::generic_types; use std::iter; +use self::primitive::*; +use self::builtin::*; + fn assemble_clauses_from_impls<'tcx>( tcx: ty::TyCtxt<'_, '_, 'tcx>, trait_def_id: DefId, @@ -49,315 +48,6 @@ fn assemble_clauses_from_assoc_ty_values<'tcx>( }); } -fn assemble_builtin_sized_impls<'tcx>( - tcx: ty::TyCtxt<'_, '_, 'tcx>, - sized_def_id: DefId, - ty: ty::Ty<'tcx>, - clauses: &mut Vec> -) { - let mut push_builtin_impl = |ty: ty::Ty<'tcx>, nested: &[ty::Ty<'tcx>]| { - let clause = ProgramClause { - goal: ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: sized_def_id, - substs: tcx.mk_substs_trait(ty, &[]), - }, - }.lower(), - hypotheses: tcx.mk_goals( - nested.iter() - .cloned() - .map(|nested_ty| ty::TraitRef { - def_id: sized_def_id, - substs: tcx.mk_substs_trait(nested_ty, &[]), - }) - .map(|trait_ref| ty::TraitPredicate { trait_ref }) - .map(|pred| GoalKind::DomainGoal(pred.lower())) - .map(|goal_kind| tcx.mk_goal(goal_kind)) - ), - category: ProgramClauseCategory::Other, - }; - // Bind innermost bound vars that may exist in `ty` and `nested`. - clauses.push(Clause::ForAll(ty::Binder::bind(clause))); - }; - - match &ty.sty { - // Non parametric primitive types. - ty::Bool | - ty::Char | - ty::Int(..) | - ty::Uint(..) | - ty::Float(..) | - ty::Error | - ty::Never => push_builtin_impl(ty, &[]), - - // These ones are always `Sized`. - &ty::Array(_, length) => { - push_builtin_impl(tcx.mk_ty(ty::Array(generic_types::bound(tcx, 0), length)), &[]); - } - ty::RawPtr(ptr) => { - push_builtin_impl(generic_types::raw_ptr(tcx, ptr.mutbl), &[]); - } - &ty::Ref(_, _, mutbl) => { - push_builtin_impl(generic_types::ref_ty(tcx, mutbl), &[]); - } - ty::FnPtr(fn_ptr) => { - let fn_ptr = fn_ptr.skip_binder(); - let fn_ptr = generic_types::fn_ptr( - tcx, - fn_ptr.inputs_and_output.len(), - fn_ptr.c_variadic, - fn_ptr.unsafety, - fn_ptr.abi - ); - push_builtin_impl(fn_ptr, &[]); - } - &ty::FnDef(def_id, ..) => { - push_builtin_impl(generic_types::fn_def(tcx, def_id), &[]); - } - &ty::Closure(def_id, ..) => { - push_builtin_impl(generic_types::closure(tcx, def_id), &[]); - } - &ty::Generator(def_id, ..) => { - push_builtin_impl(generic_types::generator(tcx, def_id), &[]); - } - - // `Sized` if the last type is `Sized` (because else we will get a WF error anyway). - &ty::Tuple(type_list) => { - let type_list = generic_types::type_list(tcx, type_list.len()); - push_builtin_impl(tcx.mk_ty(ty::Tuple(type_list)), &**type_list); - } - - // Struct def - ty::Adt(adt_def, _) => { - let substs = InternalSubsts::bound_vars_for_item(tcx, adt_def.did); - let adt = tcx.mk_ty(ty::Adt(adt_def, substs)); - let sized_constraint = adt_def.sized_constraint(tcx) - .iter() - .map(|ty| ty.subst(tcx, substs)) - .collect::>(); - push_builtin_impl(adt, &sized_constraint); - } - - // Artificially trigger an ambiguity. - ty::Infer(..) => { - // Everybody can find at least two types to unify against: - // general ty vars, int vars and float vars. - push_builtin_impl(tcx.types.i32, &[]); - push_builtin_impl(tcx.types.u32, &[]); - push_builtin_impl(tcx.types.f32, &[]); - push_builtin_impl(tcx.types.f64, &[]); - } - - ty::Projection(_projection_ty) => { - // FIXME: add builtin impls from the associated type values found in - // trait impls of `projection_ty.trait_ref(tcx)`. - } - - // The `Sized` bound can only come from the environment. - ty::Param(..) | - ty::Placeholder(..) | - ty::UnnormalizedProjection(..) => (), - - // Definitely not `Sized`. - ty::Foreign(..) | - ty::Str | - ty::Slice(..) | - ty::Dynamic(..) | - ty::Opaque(..) => (), - - ty::Bound(..) | - ty::GeneratorWitness(..) => bug!("unexpected type {:?}", ty), - } -} - -fn wf_clause_for_raw_ptr<'tcx>( - tcx: ty::TyCtxt<'_, '_, 'tcx>, - mutbl: hir::Mutability -) -> Clauses<'tcx> { - let ptr_ty = generic_types::raw_ptr(tcx, mutbl); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(ptr_ty)), - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::Implies(wf_clause); - - // `forall { WellFormed(*const T). }` - tcx.mk_clauses(iter::once(wf_clause)) -} - -fn wf_clause_for_fn_ptr<'tcx>( - tcx: ty::TyCtxt<'_, '_, 'tcx>, - arity_and_output: usize, - c_variadic: bool, - unsafety: hir::Unsafety, - abi: abi::Abi -) -> Clauses<'tcx> { - let fn_ptr = generic_types::fn_ptr(tcx, arity_and_output, c_variadic, unsafety, abi); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(fn_ptr)), - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // `forall { WellFormed(for<> fn(T1, ..., Tn) -> Tn+1). }` - // where `n + 1` == `arity_and_output` - tcx.mk_clauses(iter::once(wf_clause)) -} - -fn wf_clause_for_slice<'tcx>(tcx: ty::TyCtxt<'_, '_, 'tcx>) -> Clauses<'tcx> { - let ty = generic_types::bound(tcx, 0); - let slice_ty = tcx.mk_slice(ty); - - let sized_trait = match tcx.lang_items().sized_trait() { - Some(def_id) => def_id, - None => return ty::List::empty(), - }; - let sized_implemented = ty::TraitRef { - def_id: sized_trait, - substs: tcx.mk_substs_trait(ty, ty::List::empty()), - }; - let sized_implemented: DomainGoal<'_> = ty::TraitPredicate { - trait_ref: sized_implemented - }.lower(); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(slice_ty)), - hypotheses: tcx.mk_goals( - iter::once(tcx.mk_goal(GoalKind::DomainGoal(sized_implemented))) - ), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // `forall { WellFormed([T]) :- Implemented(T: Sized). }` - tcx.mk_clauses(iter::once(wf_clause)) -} - -fn wf_clause_for_array<'tcx>( - tcx: ty::TyCtxt<'_, '_, 'tcx>, - length: &'tcx ty::Const<'tcx> -) -> Clauses<'tcx> { - let ty = generic_types::bound(tcx, 0); - let array_ty = tcx.mk_ty(ty::Array(ty, length)); - - let sized_trait = match tcx.lang_items().sized_trait() { - Some(def_id) => def_id, - None => return ty::List::empty(), - }; - let sized_implemented = ty::TraitRef { - def_id: sized_trait, - substs: tcx.mk_substs_trait(ty, ty::List::empty()), - }; - let sized_implemented: DomainGoal<'_> = ty::TraitPredicate { - trait_ref: sized_implemented - }.lower(); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(array_ty)), - hypotheses: tcx.mk_goals( - iter::once(tcx.mk_goal(GoalKind::DomainGoal(sized_implemented))) - ), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // `forall { WellFormed([T; length]) :- Implemented(T: Sized). }` - tcx.mk_clauses(iter::once(wf_clause)) -} - -fn wf_clause_for_tuple<'tcx>( - tcx: ty::TyCtxt<'_, '_, 'tcx>, - arity: usize -) -> Clauses<'tcx> { - let type_list = generic_types::type_list(tcx, arity); - let tuple_ty = tcx.mk_ty(ty::Tuple(type_list)); - - let sized_trait = match tcx.lang_items().sized_trait() { - Some(def_id) => def_id, - None => return ty::List::empty(), - }; - - // If `arity == 0` (i.e. the unit type) or `arity == 1`, this list of - // hypotheses is actually empty. - let sized_implemented = type_list[0 .. std::cmp::max(arity, 1) - 1].iter() - .map(|ty| ty::TraitRef { - def_id: sized_trait, - substs: tcx.mk_substs_trait(*ty, ty::List::empty()), - }) - .map(|trait_ref| ty::TraitPredicate { trait_ref }) - .map(|predicate| predicate.lower()); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(tuple_ty)), - hypotheses: tcx.mk_goals( - sized_implemented.map(|domain_goal| { - tcx.mk_goal(GoalKind::DomainGoal(domain_goal)) - }) - ), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // ``` - // forall { - // WellFormed((T1, ..., Tn)) :- - // Implemented(T1: Sized), - // ... - // Implemented(Tn-1: Sized). - // } - // ``` - tcx.mk_clauses(iter::once(wf_clause)) -} - -fn wf_clause_for_ref<'tcx>( - tcx: ty::TyCtxt<'_, '_, 'tcx>, - mutbl: hir::Mutability -) -> Clauses<'tcx> { - let region = tcx.mk_region( - ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(0)) - ); - let ty = generic_types::bound(tcx, 1); - let ref_ty = tcx.mk_ref(region, ty::TypeAndMut { - ty, - mutbl, - }); - - let outlives: DomainGoal<'_> = ty::OutlivesPredicate(ty, region).lower(); - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(ref_ty)), - hypotheses: tcx.mk_goals( - iter::once(tcx.mk_goal(outlives.into_goal())) - ), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // `forall<'a, T> { WellFormed(&'a T) :- Outlives(T: 'a). }` - tcx.mk_clauses(iter::once(wf_clause)) -} - -fn wf_clause_for_fn_def<'tcx>( - tcx: ty::TyCtxt<'_, '_, 'tcx>, - def_id: DefId -) -> Clauses<'tcx> { - let fn_def = generic_types::fn_def(tcx, def_id); - - let wf_clause = ProgramClause { - goal: DomainGoal::WellFormed(WellFormed::Ty(fn_def)), - hypotheses: ty::List::empty(), - category: ProgramClauseCategory::WellFormed, - }; - let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); - - // `forall { WellFormed(fn some_fn(T1, ..., Tn) -> Tn+1). }` - // where `def_id` maps to the `some_fn` function definition - tcx.mk_clauses(iter::once(wf_clause)) -} - impl ChalkInferenceContext<'cx, 'gcx, 'tcx> { pub(super) fn program_clauses_impl( &self, @@ -394,10 +84,8 @@ impl ChalkInferenceContext<'cx, 'gcx, 'tcx> { ); } - // FIXME: we need to add special rules for builtin impls: + // FIXME: we need to add special rules for other builtin impls: // * `Copy` / `Clone` - // * `Sized` - // * `Unsize` // * `Generator` // * `FnOnce` / `FnMut` / `Fn` // * trait objects diff --git a/src/librustc_traits/chalk_context/program_clauses/primitive.rs b/src/librustc_traits/chalk_context/program_clauses/primitive.rs new file mode 100644 index 0000000000000..fc6d40914745d --- /dev/null +++ b/src/librustc_traits/chalk_context/program_clauses/primitive.rs @@ -0,0 +1,204 @@ +use rustc::traits::{ + WellFormed, + DomainGoal, + GoalKind, + Clause, + Clauses, + ProgramClause, + ProgramClauseCategory, +}; +use rustc::ty; +use rustc::hir; +use rustc::hir::def_id::DefId; +use rustc_target::spec::abi; +use crate::lowering::Lower; +use crate::generic_types; +use std::iter; + +crate fn wf_clause_for_raw_ptr<'tcx>( + tcx: ty::TyCtxt<'_, '_, 'tcx>, + mutbl: hir::Mutability +) -> Clauses<'tcx> { + let ptr_ty = generic_types::raw_ptr(tcx, mutbl); + + let wf_clause = ProgramClause { + goal: DomainGoal::WellFormed(WellFormed::Ty(ptr_ty)), + hypotheses: ty::List::empty(), + category: ProgramClauseCategory::WellFormed, + }; + let wf_clause = Clause::Implies(wf_clause); + + // `forall { WellFormed(*const T). }` + tcx.mk_clauses(iter::once(wf_clause)) +} + +crate fn wf_clause_for_fn_ptr<'tcx>( + tcx: ty::TyCtxt<'_, '_, 'tcx>, + arity_and_output: usize, + variadic: bool, + unsafety: hir::Unsafety, + abi: abi::Abi +) -> Clauses<'tcx> { + let fn_ptr = generic_types::fn_ptr(tcx, arity_and_output, variadic, unsafety, abi); + + let wf_clause = ProgramClause { + goal: DomainGoal::WellFormed(WellFormed::Ty(fn_ptr)), + hypotheses: ty::List::empty(), + category: ProgramClauseCategory::WellFormed, + }; + let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); + + // `forall { WellFormed(for<> fn(T1, ..., Tn) -> Tn+1). }` + // where `n + 1` == `arity_and_output` + tcx.mk_clauses(iter::once(wf_clause)) +} + +crate fn wf_clause_for_slice<'tcx>(tcx: ty::TyCtxt<'_, '_, 'tcx>) -> Clauses<'tcx> { + let ty = generic_types::bound(tcx, 0); + let slice_ty = tcx.mk_slice(ty); + + let sized_trait = match tcx.lang_items().sized_trait() { + Some(def_id) => def_id, + None => return ty::List::empty(), + }; + let sized_implemented = ty::TraitRef { + def_id: sized_trait, + substs: tcx.mk_substs_trait(ty, ty::List::empty()), + }; + let sized_implemented: DomainGoal<'_> = ty::TraitPredicate { + trait_ref: sized_implemented + }.lower(); + + let wf_clause = ProgramClause { + goal: DomainGoal::WellFormed(WellFormed::Ty(slice_ty)), + hypotheses: tcx.mk_goals( + iter::once(tcx.mk_goal(GoalKind::DomainGoal(sized_implemented))) + ), + category: ProgramClauseCategory::WellFormed, + }; + let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); + + // `forall { WellFormed([T]) :- Implemented(T: Sized). }` + tcx.mk_clauses(iter::once(wf_clause)) +} + +crate fn wf_clause_for_array<'tcx>( + tcx: ty::TyCtxt<'_, '_, 'tcx>, + length: &'tcx ty::Const<'tcx> +) -> Clauses<'tcx> { + let ty = generic_types::bound(tcx, 0); + let array_ty = tcx.mk_ty(ty::Array(ty, length)); + + let sized_trait = match tcx.lang_items().sized_trait() { + Some(def_id) => def_id, + None => return ty::List::empty(), + }; + let sized_implemented = ty::TraitRef { + def_id: sized_trait, + substs: tcx.mk_substs_trait(ty, ty::List::empty()), + }; + let sized_implemented: DomainGoal<'_> = ty::TraitPredicate { + trait_ref: sized_implemented + }.lower(); + + let wf_clause = ProgramClause { + goal: DomainGoal::WellFormed(WellFormed::Ty(array_ty)), + hypotheses: tcx.mk_goals( + iter::once(tcx.mk_goal(GoalKind::DomainGoal(sized_implemented))) + ), + category: ProgramClauseCategory::WellFormed, + }; + let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); + + // `forall { WellFormed([T; length]) :- Implemented(T: Sized). }` + tcx.mk_clauses(iter::once(wf_clause)) +} + +crate fn wf_clause_for_tuple<'tcx>( + tcx: ty::TyCtxt<'_, '_, 'tcx>, + arity: usize +) -> Clauses<'tcx> { + let type_list = generic_types::type_list(tcx, arity); + let tuple_ty = tcx.mk_ty(ty::Tuple(type_list)); + + let sized_trait = match tcx.lang_items().sized_trait() { + Some(def_id) => def_id, + None => return ty::List::empty(), + }; + + // If `arity == 0` (i.e. the unit type) or `arity == 1`, this list of + // hypotheses is actually empty. + let sized_implemented = type_list[0 .. std::cmp::max(arity, 1) - 1].iter() + .map(|ty| ty::TraitRef { + def_id: sized_trait, + substs: tcx.mk_substs_trait(*ty, ty::List::empty()), + }) + .map(|trait_ref| ty::TraitPredicate { trait_ref }) + .map(|predicate| predicate.lower()); + + let wf_clause = ProgramClause { + goal: DomainGoal::WellFormed(WellFormed::Ty(tuple_ty)), + hypotheses: tcx.mk_goals( + sized_implemented.map(|domain_goal| { + tcx.mk_goal(GoalKind::DomainGoal(domain_goal)) + }) + ), + category: ProgramClauseCategory::WellFormed, + }; + let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); + + // ``` + // forall { + // WellFormed((T1, ..., Tn)) :- + // Implemented(T1: Sized), + // ... + // Implemented(Tn-1: Sized). + // } + // ``` + tcx.mk_clauses(iter::once(wf_clause)) +} + +crate fn wf_clause_for_ref<'tcx>( + tcx: ty::TyCtxt<'_, '_, 'tcx>, + mutbl: hir::Mutability +) -> Clauses<'tcx> { + let region = tcx.mk_region( + ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(0)) + ); + let ty = generic_types::bound(tcx, 1); + let ref_ty = tcx.mk_ref(region, ty::TypeAndMut { + ty, + mutbl, + }); + + let outlives: DomainGoal<'_> = ty::OutlivesPredicate(ty, region).lower(); + let wf_clause = ProgramClause { + goal: DomainGoal::WellFormed(WellFormed::Ty(ref_ty)), + hypotheses: tcx.mk_goals( + iter::once(tcx.mk_goal(outlives.into_goal())) + ), + category: ProgramClauseCategory::WellFormed, + }; + let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); + + // `forall<'a, T> { WellFormed(&'a T) :- Outlives(T: 'a). }` + tcx.mk_clauses(iter::once(wf_clause)) +} + +crate fn wf_clause_for_fn_def<'tcx>( + tcx: ty::TyCtxt<'_, '_, 'tcx>, + def_id: DefId +) -> Clauses<'tcx> { + let fn_def = generic_types::fn_def(tcx, def_id); + + let wf_clause = ProgramClause { + goal: DomainGoal::WellFormed(WellFormed::Ty(fn_def)), + hypotheses: ty::List::empty(), + category: ProgramClauseCategory::WellFormed, + }; + let wf_clause = Clause::ForAll(ty::Binder::bind(wf_clause)); + + // `forall { WellFormed(fn some_fn(T1, ..., Tn) -> Tn+1). }` + // where `def_id` maps to the `some_fn` function definition + tcx.mk_clauses(iter::once(wf_clause)) +} From 183466550808dbce73709a2202d5f84fdfb8ddc1 Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 8 Feb 2019 13:10:52 +0100 Subject: [PATCH 4/9] Add unsize impls for arrays --- .../chalk_context/program_clauses/builtin.rs | 76 +++++++++++++++++++ .../chalk_context/program_clauses/mod.rs | 12 +++ 2 files changed, 88 insertions(+) diff --git a/src/librustc_traits/chalk_context/program_clauses/builtin.rs b/src/librustc_traits/chalk_context/program_clauses/builtin.rs index 3622cacbb012e..ae9f1a27b9422 100644 --- a/src/librustc_traits/chalk_context/program_clauses/builtin.rs +++ b/src/librustc_traits/chalk_context/program_clauses/builtin.rs @@ -10,6 +10,82 @@ use rustc::hir::def_id::DefId; use crate::lowering::Lower; use crate::generic_types; +crate fn assemble_builtin_unsize_impls<'tcx>( + tcx: ty::TyCtxt<'_, '_, 'tcx>, + unsize_def_id: DefId, + source: ty::Ty<'tcx>, + target: ty::Ty<'tcx>, + clauses: &mut Vec> +) { + match (&source.sty, &target.sty) { + (ty::Dynamic(data_a, ..), ty::Dynamic(data_b, ..)) => { + if data_a.principal_def_id() != data_b.principal_def_id() + || data_b.auto_traits().any(|b| data_a.auto_traits().all(|a| a != b)) + { + return; + } + + // FIXME: rules for trait upcast + } + + (_, &ty::Dynamic(..)) => { + // FIXME: basically, we should have something like: + // ``` + // forall { + // Implemented(T: Unsize< for<...> dyn Trait<...> >) :- + // for<...> Implemented(T: Trait<...>). + // } + // ``` + // The question is: how to correctly handle the higher-ranked + // `for<...>` binder in order to have a generic rule? + // (Having generic rules is useful for caching, as we may be able + // to turn this function and others into tcx queries later on). + } + + (ty::Array(_, length), ty::Slice(_)) => { + let ty_param = generic_types::bound(tcx, 0); + let array_ty = tcx.mk_ty(ty::Array(ty_param, length)); + let slice_ty = tcx.mk_ty(ty::Slice(ty_param)); + + // `forall { Implemented([T; N]: Unsize<[T]>). }` + let clause = ProgramClause { + goal: ty::TraitPredicate { + trait_ref: ty::TraitRef { + def_id: unsize_def_id, + substs: tcx.mk_substs_trait(array_ty, &[slice_ty.into()]) + }, + }.lower(), + hypotheses: ty::List::empty(), + category: ProgramClauseCategory::Other, + }; + + clauses.push(Clause::ForAll(ty::Binder::bind(clause))); + } + + (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => { + // FIXME: ambiguous + } + + (ty::Adt(def_id_a, ..), ty::Adt(def_id_b, ..)) => { + if def_id_a != def_id_b { + return; + } + + // FIXME: rules for struct unsizing + } + + (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { + if tys_a.len() != tys_b.len() { + return; + } + + // FIXME: rules for tuple unsizing + } + + _ => (), + } +} + crate fn assemble_builtin_sized_impls<'tcx>( tcx: ty::TyCtxt<'_, '_, 'tcx>, sized_def_id: DefId, diff --git a/src/librustc_traits/chalk_context/program_clauses/mod.rs b/src/librustc_traits/chalk_context/program_clauses/mod.rs index b6fb70b057779..80fbd97c5876b 100644 --- a/src/librustc_traits/chalk_context/program_clauses/mod.rs +++ b/src/librustc_traits/chalk_context/program_clauses/mod.rs @@ -84,6 +84,18 @@ impl ChalkInferenceContext<'cx, 'gcx, 'tcx> { ); } + if Some(trait_predicate.def_id()) == self.infcx.tcx.lang_items().unsize_trait() { + let source = trait_predicate.self_ty(); + let target = trait_predicate.trait_ref.substs.type_at(1); + assemble_builtin_unsize_impls( + self.infcx.tcx, + trait_predicate.def_id(), + source, + target, + &mut clauses + ); + } + // FIXME: we need to add special rules for other builtin impls: // * `Copy` / `Clone` // * `Generator` From 4effdd2431020ce86d4c22765afa05d19c3215cc Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 8 Feb 2019 15:29:22 +0100 Subject: [PATCH 5/9] Fix a bug in chalk lowering Some where clauses were not subtituted for generic bound type vars. --- src/librustc_traits/lowering/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/librustc_traits/lowering/mod.rs b/src/librustc_traits/lowering/mod.rs index 44883d438a1e5..0cbfa7c626f13 100644 --- a/src/librustc_traits/lowering/mod.rs +++ b/src/librustc_traits/lowering/mod.rs @@ -209,6 +209,10 @@ fn program_clauses_for_trait<'a, 'tcx>( let implemented_from_env = Clause::ForAll(ty::Binder::bind(implemented_from_env)); let predicates = &tcx.predicates_defined_on(def_id).predicates; + + // Warning: these where clauses are not substituted for bound vars yet, + // so that we don't need to adjust binders in the `FromEnv` rules below + // (see the FIXME). let where_clauses = &predicates .iter() .map(|(wc, _)| wc.lower()) @@ -258,6 +262,7 @@ fn program_clauses_for_trait<'a, 'tcx>( // `WellFormed(WC)` let wf_conditions = where_clauses .into_iter() + .map(|wc| wc.subst(tcx, bound_vars)) .map(|wc| wc.map_bound(|goal| goal.into_well_formed_goal())); // `WellFormed(Self: Trait) :- Implemented(Self: Trait) && WellFormed(WC)` @@ -341,7 +346,9 @@ pub fn program_clauses_for_type_def<'a, 'tcx>( // `Ty<...>` let ty = tcx.type_of(def_id).subst(tcx, bound_vars); - // `WC` + // Warning: these where clauses are not substituted for bound vars yet, + // so that we don't need to adjust binders in the `FromEnv` rules below + // (see the FIXME). let where_clauses = tcx.predicates_of(def_id).predicates .iter() .map(|(wc, _)| wc.lower()) From d6a2b7c470c91badc8ac2f1e20b40497086c6a66 Mon Sep 17 00:00:00 2001 From: scalexm Date: Fri, 8 Feb 2019 15:37:26 +0100 Subject: [PATCH 6/9] Fix a bug in implied bounds --- src/librustc_traits/lowering/mod.rs | 9 +++--- .../recursive_where_clause_on_type.rs | 28 +++++++++++++++++++ src/test/ui/chalkify/lower_env2.stderr | 2 +- src/test/ui/chalkify/lower_struct.stderr | 2 +- src/test/ui/chalkify/lower_trait.stderr | 2 +- 5 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 src/test/compile-fail/chalkify/recursive_where_clause_on_type.rs diff --git a/src/librustc_traits/lowering/mod.rs b/src/librustc_traits/lowering/mod.rs index 0cbfa7c626f13..c3cbdb0376251 100644 --- a/src/librustc_traits/lowering/mod.rs +++ b/src/librustc_traits/lowering/mod.rs @@ -337,7 +337,7 @@ pub fn program_clauses_for_type_def<'a, 'tcx>( // // ``` // forall { - // WellFormed(Ty<...>) :- WC1, ..., WCm` + // WellFormed(Ty<...>) :- WellFormed(WC1), ..., WellFormed(WCm)` // } // ``` @@ -354,13 +354,14 @@ pub fn program_clauses_for_type_def<'a, 'tcx>( .map(|(wc, _)| wc.lower()) .collect::>(); - // `WellFormed(Ty<...>) :- WC1, ..., WCm` + // `WellFormed(Ty<...>) :- WellFormed(WC1), ..., WellFormed(WCm)` let well_formed_clause = ProgramClause { goal: DomainGoal::WellFormed(WellFormed::Ty(ty)), hypotheses: tcx.mk_goals( where_clauses .iter() .map(|wc| wc.subst(tcx, bound_vars)) + .map(|wc| wc.map_bound(|bound| bound.into_well_formed_goal())) .map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))), ), category: ProgramClauseCategory::WellFormed, @@ -457,13 +458,13 @@ pub fn program_clauses_for_associated_type_def<'a, 'tcx>( // ``` // forall { // WellFormed((Trait::AssocType)) - // :- Implemented(Self: Trait) + // :- WellFormed(Self: Trait) // } // ``` let trait_predicate = ty::TraitPredicate { trait_ref }; let hypothesis = tcx.mk_goal( - DomainGoal::Holds(WhereClause::Implemented(trait_predicate)).into_goal() + DomainGoal::WellFormed(WellFormed::Trait(trait_predicate)).into_goal() ); let wf_clause = ProgramClause { diff --git a/src/test/compile-fail/chalkify/recursive_where_clause_on_type.rs b/src/test/compile-fail/chalkify/recursive_where_clause_on_type.rs new file mode 100644 index 0000000000000..861f86e61658a --- /dev/null +++ b/src/test/compile-fail/chalkify/recursive_where_clause_on_type.rs @@ -0,0 +1,28 @@ +// compile-flags: -Z chalk + +#![feature(trivial_bounds)] + +trait Bar { + fn foo(); +} +trait Foo: Bar { } + +struct S where S: Foo; + +impl Foo for S { +} + +fn bar() { + T::foo(); +} + +fn foo() { + bar::() +} + +fn main() { + // For some reason, the error is duplicated... + + foo::() //~ ERROR the type `S` is not well-formed (chalk) + //~^ ERROR the type `S` is not well-formed (chalk) +} diff --git a/src/test/ui/chalkify/lower_env2.stderr b/src/test/ui/chalkify/lower_env2.stderr index 2a71fa9df5e8e..613a568a8549c 100644 --- a/src/test/ui/chalkify/lower_env2.stderr +++ b/src/test/ui/chalkify/lower_env2.stderr @@ -6,7 +6,7 @@ LL | #[rustc_dump_program_clauses] | = note: forall<'a, T> { FromEnv(T: Foo) :- FromEnv(S<'a, T>). } = note: forall<'a, T> { TypeOutlives(T: 'a) :- FromEnv(S<'a, T>). } - = note: forall<'a, T> { WellFormed(S<'a, T>) :- Implemented(T: Foo), TypeOutlives(T: 'a). } + = note: forall<'a, T> { WellFormed(S<'a, T>) :- WellFormed(T: Foo), TypeOutlives(T: 'a). } error: program clause dump --> $DIR/lower_env2.rs:11:1 diff --git a/src/test/ui/chalkify/lower_struct.stderr b/src/test/ui/chalkify/lower_struct.stderr index 91525c3ba55d4..0331c2fca16db 100644 --- a/src/test/ui/chalkify/lower_struct.stderr +++ b/src/test/ui/chalkify/lower_struct.stderr @@ -7,7 +7,7 @@ LL | #[rustc_dump_program_clauses] = note: forall<'a, T> { FromEnv(T: std::marker::Sized) :- FromEnv(Foo<'a, T>). } = note: forall<'a, T> { FromEnv(std::boxed::Box: std::clone::Clone) :- FromEnv(Foo<'a, T>). } = note: forall<'a, T> { TypeOutlives(T: 'a) :- FromEnv(Foo<'a, T>). } - = note: forall<'a, T> { WellFormed(Foo<'a, T>) :- Implemented(T: std::marker::Sized), Implemented(std::boxed::Box: std::clone::Clone), TypeOutlives(T: 'a). } + = note: forall<'a, T> { WellFormed(Foo<'a, T>) :- WellFormed(T: std::marker::Sized), WellFormed(std::boxed::Box: std::clone::Clone), TypeOutlives(T: 'a). } error: aborting due to previous error diff --git a/src/test/ui/chalkify/lower_trait.stderr b/src/test/ui/chalkify/lower_trait.stderr index 423c557308301..ed3bded398ae3 100644 --- a/src/test/ui/chalkify/lower_trait.stderr +++ b/src/test/ui/chalkify/lower_trait.stderr @@ -18,7 +18,7 @@ LL | #[rustc_dump_program_clauses] = note: forall { ProjectionEq(>::Assoc == ^3) :- Normalize(>::Assoc -> ^3). } = note: forall { FromEnv(Self: Foo) :- FromEnv(Unnormalized(>::Assoc)). } = note: forall { ProjectionEq(>::Assoc == Unnormalized(>::Assoc)). } - = note: forall { WellFormed(Unnormalized(>::Assoc)) :- Implemented(Self: Foo). } + = note: forall { WellFormed(Unnormalized(>::Assoc)) :- WellFormed(Self: Foo). } error: aborting due to 2 previous errors From 356ea0b405f08bd6a8fcff1138088802747a5ee9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 20 Mar 2019 12:45:03 -0400 Subject: [PATCH 7/9] nll_relate/mod.rs: rustfmt --- src/librustc/infer/nll_relate/mod.rs | 52 +++++++++++++++------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index 2db3208953eb0..1767db3ff0373 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs @@ -22,12 +22,12 @@ //! constituents) use crate::infer::InferCtxt; +use crate::traits::DomainGoal; +use crate::ty::error::TypeError; use crate::ty::fold::{TypeFoldable, TypeVisitor}; use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; use crate::ty::subst::Kind; use crate::ty::{self, Ty, TyCtxt}; -use crate::ty::error::TypeError; -use crate::traits::DomainGoal; use rustc_data_structures::fx::FxHashMap; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -266,7 +266,7 @@ where fn relate_projection_ty( &mut self, projection_ty: ty::ProjectionTy<'tcx>, - value_ty: ty::Ty<'tcx> + value_ty: ty::Ty<'tcx>, ) -> Ty<'tcx> { use crate::infer::type_variable::TypeVariableOrigin; use crate::traits::WhereClause; @@ -274,7 +274,9 @@ where match value_ty.sty { ty::Projection(other_projection_ty) => { - let var = self.infcx.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP)); + let var = self + .infcx + .next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP)); self.relate_projection_ty(projection_ty, var); self.relate_projection_ty(other_projection_ty, var); var @@ -285,9 +287,8 @@ where projection_ty, ty: value_ty, }; - self.delegate.push_domain_goal( - DomainGoal::Holds(WhereClause::ProjectionEq(projection)) - ); + self.delegate + .push_domain_goal(DomainGoal::Holds(WhereClause::ProjectionEq(projection))); value_ty } } @@ -297,20 +298,21 @@ where fn relate_ty_var( &mut self, vid: ty::TyVid, - value_ty: Ty<'tcx> + value_ty: Ty<'tcx>, ) -> RelateResult<'tcx, Ty<'tcx>> { debug!("relate_ty_var(vid={:?}, value_ty={:?})", vid, value_ty); match value_ty.sty { ty::Infer(ty::TyVar(value_vid)) => { // Two type variables: just equate them. - self.infcx.type_variables.borrow_mut().equate(vid, value_vid); + self.infcx + .type_variables + .borrow_mut() + .equate(vid, value_vid); return Ok(value_ty); } - ty::Projection(projection_ty) - if D::normalization() == NormalizationStrategy::Lazy => - { + ty::Projection(projection_ty) if D::normalization() == NormalizationStrategy::Lazy => { return Ok(self.relate_projection_ty(projection_ty, self.infcx.tcx.mk_ty_var(vid))); } @@ -327,7 +329,10 @@ where assert!(!generalized_ty.has_infer_types()); } - self.infcx.type_variables.borrow_mut().instantiate(vid, generalized_ty); + self.infcx + .type_variables + .borrow_mut() + .instantiate(vid, generalized_ty); // The generalized values we extract from `canonical_var_values` have // been fully instantiated and hence the set of scopes we have @@ -348,7 +353,7 @@ where fn generalize_value>( &mut self, value: T, - for_vid: ty::TyVid + for_vid: ty::TyVid, ) -> RelateResult<'tcx, T> { let universe = self.infcx.probe_ty_var(for_vid).unwrap_err(); @@ -764,7 +769,9 @@ where drop(variables); self.relate(&u, &u) } - TypeVariableValue::Unknown { universe: _universe } => { + TypeVariableValue::Unknown { + universe: _universe, + } => { if self.ambient_variance == ty::Bivariant { // FIXME: we may need a WF predicate (related to #54105). } @@ -779,8 +786,7 @@ where let u = self.tcx().mk_ty_var(new_var_id); debug!( "generalize: replacing original vid={:?} with new={:?}", - vid, - u + vid, u ); return Ok(u); } @@ -788,8 +794,7 @@ where } } - ty::Infer(ty::IntVar(_)) | - ty::Infer(ty::FloatVar(_)) => { + ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) => { // No matter what mode we are in, // integer/floating-point types must be equal to be // relatable. @@ -800,9 +805,8 @@ where if self.universe.cannot_name(placeholder.universe) { debug!( "TypeGeneralizer::tys: root universe {:?} cannot name\ - placeholder in universe {:?}", - self.universe, - placeholder.universe + placeholder in universe {:?}", + self.universe, placeholder.universe ); Err(TypeError::Mismatch) } else { @@ -810,9 +814,7 @@ where } } - _ => { - relate::super_relate_tys(self, a, a) - } + _ => relate::super_relate_tys(self, a, a), } } From 0cb7926e05a1bb53f7db49e9a989c27155152b41 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 20 Mar 2019 12:45:24 -0400 Subject: [PATCH 8/9] refactor so that `relate_ty_var` can accommodate vid on a or b side --- src/librustc/infer/nll_relate/mod.rs | 152 +++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 22 deletions(-) diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index 1767db3ff0373..735dcffc6d16d 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs @@ -29,6 +29,7 @@ use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; use crate::ty::subst::Kind; use crate::ty::{self, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; +use std::fmt::Debug; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum NormalizationStrategy { @@ -294,14 +295,34 @@ where } } - /// Relate a type inference variable with a value type. - fn relate_ty_var( + /// Relate a type inference variable with a value type. This works + /// by creating a "generalization" G of the value where all the + /// lifetimes are replaced with fresh inference values. This + /// genearlization G becomes the value of the inference variable, + /// and is then related in turn to the value. So e.g. if you had + /// `vid = ?0` and `value = &'a u32`, we might first instantiate + /// `?0` to a type like `&'0 u32` where `'0` is a fresh variable, + /// and then relate `&'0 u32` with `&'a u32` (resulting in + /// relations between `'0` and `'a`). + /// + /// The variable `pair` can be either a `(vid, ty)` or `(ty, vid)` + /// -- in other words, it is always a (unresolved) inference + /// variable `vid` and a type `ty` that are being related, but the + /// vid may appear either as the "a" type or the "b" type, + /// depending on where it appears in the tuple. The trait + /// `VidValuePair` lets us work with the vid/type while preserving + /// the "sidedness" when necessary -- the sidedness is relevant in + /// particular for the variance and set of in-scope things. + fn relate_ty_var>( &mut self, - vid: ty::TyVid, - value_ty: Ty<'tcx>, + pair: PAIR, ) -> RelateResult<'tcx, Ty<'tcx>> { - debug!("relate_ty_var(vid={:?}, value_ty={:?})", vid, value_ty); + debug!("relate_ty_var({:?})", pair); + + let vid = pair.vid(); + let value_ty = pair.value_ty(); + // FIXME -- this logic assumes invariance, but that is wrong match value_ty.sty { ty::Infer(ty::TyVar(value_vid)) => { // Two type variables: just equate them. @@ -338,13 +359,13 @@ where // been fully instantiated and hence the set of scopes we have // doesn't matter -- just to be sure, put an empty vector // in there. - let old_a_scopes = ::std::mem::replace(&mut self.a_scopes, vec![]); + let old_a_scopes = ::std::mem::replace(pair.vid_scopes(self), vec![]); // Relate the generalized kind to the original one. - let result = self.relate(&generalized_ty, &value_ty); + let result = pair.relate_generalized_ty(self, generalized_ty); // Restore the old scopes now. - self.a_scopes = old_a_scopes; + *pair.vid_scopes(self) = old_a_scopes; debug!("relate_ty_var: complete, result = {:?}", result); result @@ -370,6 +391,104 @@ where } } +/// When we instantiate a inference variable with a value in +/// `relate_ty_var`, we always have the pair of a `TyVid` and a `Ty`, +/// but the ordering may vary (depending on whether the inference +/// variable was found on the `a` or `b` sides). Therefore, this trait +/// allows us to factor out common code, while preserving the order +/// when needed. +trait VidValuePair<'tcx>: Debug { + /// Extract the inference variable (which could be either the + /// first or second part of the tuple). + fn vid(&self) -> ty::TyVid; + + /// Extract the value it is being related to (which will be the + /// opposite part of the tuple from the vid). + fn value_ty(&self) -> Ty<'tcx>; + + /// Extract the scopes that apply to whichever side of the tuple + /// the vid was found on. See the comment where this is called + /// for more details on why we want them. + fn vid_scopes>( + &self, + relate: &'r mut TypeRelating<'_, '_, 'tcx, D>, + ) -> &'r mut Vec>; + + /// Given a generalized type G that should replace the vid, relate + /// G to the value, putting G on whichever side the vid would have + /// appeared. + fn relate_generalized_ty( + &self, + relate: &mut TypeRelating<'_, '_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>; +} + +impl VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { + fn vid(&self) -> ty::TyVid { + self.0 + } + + fn value_ty(&self) -> Ty<'tcx> { + self.1 + } + + fn vid_scopes( + &self, + relate: &'r mut TypeRelating<'_, '_, 'tcx, D>, + ) -> &'r mut Vec> + where + D: TypeRelatingDelegate<'tcx>, + { + &mut relate.a_scopes + } + + fn relate_generalized_ty( + &self, + relate: &mut TypeRelating<'_, '_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>, + { + relate.relate(&generalized_ty, &self.value_ty()) + } +} + +// In this case, the "vid" is the "b" type. +impl VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { + fn vid(&self) -> ty::TyVid { + self.1 + } + + fn value_ty(&self) -> Ty<'tcx> { + self.0 + } + + fn vid_scopes( + &self, + relate: &'r mut TypeRelating<'_, '_, 'tcx, D>, + ) -> &'r mut Vec> + where + D: TypeRelatingDelegate<'tcx>, + { + &mut relate.b_scopes + } + + fn relate_generalized_ty( + &self, + relate: &mut TypeRelating<'_, '_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>, + { + relate.relate(&self.value_ty(), &generalized_ty) + } +} + impl TypeRelation<'me, 'gcx, 'tcx> for TypeRelating<'me, 'gcx, 'tcx, D> where D: TypeRelatingDelegate<'tcx>, @@ -427,17 +546,11 @@ where // Forbid inference variables in the RHS. bug!("unexpected inference var {:?}", b) } else { - // We swap the order of `a` and `b` in the call to - // `relate_ty_var` below, so swap the corresponding scopes - // as well. - std::mem::swap(&mut self.a_scopes, &mut self.b_scopes); - let res = self.relate_ty_var(vid, a); - std::mem::swap(&mut self.a_scopes, &mut self.b_scopes); - res + self.relate_ty_var((a, vid)) } } - (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var(vid, b), + (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)), (&ty::Projection(projection_ty), _) if D::normalization() == NormalizationStrategy::Lazy => @@ -448,12 +561,7 @@ where (_, &ty::Projection(projection_ty)) if D::normalization() == NormalizationStrategy::Lazy => { - // Swap the respective scopes of `a` and `b` (see comment - // above). - std::mem::swap(&mut self.a_scopes, &mut self.b_scopes); - let res = self.relate_projection_ty(projection_ty, a); - std::mem::swap(&mut self.a_scopes, &mut self.b_scopes); - Ok(res) + Ok(self.relate_projection_ty(projection_ty, a)) } _ => { From ca5a2122cc68fa14e72f1631ee5e9bbf2ac9c94f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 20 Mar 2019 12:52:36 -0400 Subject: [PATCH 9/9] expand the fixme --- src/librustc/infer/nll_relate/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index 735dcffc6d16d..d8e7328c274b7 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs @@ -322,7 +322,10 @@ where let vid = pair.vid(); let value_ty = pair.value_ty(); - // FIXME -- this logic assumes invariance, but that is wrong + // FIXME -- this logic assumes invariance, but that is wrong. + // This only presently applies to chalk integration, as NLL + // doesn't permit type variables to appear on both sides (and + // doesn't use lazy norm). match value_ty.sty { ty::Infer(ty::TyVar(value_vid)) => { // Two type variables: just equate them.