From cb2308de549975f538bc51325481ad235cea4e9d Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Sat, 23 May 2020 15:29:51 +0200 Subject: [PATCH] implement wf checking for constants --- src/librustc_trait_selection/traits/wf.rs | 125 +++++++++++++++------- 1 file changed, 87 insertions(+), 38 deletions(-) diff --git a/src/librustc_trait_selection/traits/wf.rs b/src/librustc_trait_selection/traits/wf.rs index 39c7528a632..60a88815214 100644 --- a/src/librustc_trait_selection/traits/wf.rs +++ b/src/librustc_trait_selection/traits/wf.rs @@ -4,7 +4,7 @@ use crate::traits; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items; -use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_span::Span; use std::rc::Rc; @@ -37,7 +37,7 @@ pub fn obligations<'a, 'tcx>( }; let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; - wf.compute(ty); + wf.compute(ty.into()); debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out); let result = wf.normalize(); @@ -45,6 +45,36 @@ pub fn obligations<'a, 'tcx>( Some(result) } +/// Returns the set of obligations needed to make the `constant` well-formed. +pub fn const_obligations<'a, 'tcx>( + infcx: &InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + constant: &'tcx ty::Const<'tcx>, + span: Span, +) -> Option>> { + let constant = match constant.val { + ty::ConstKind::Infer(infer) => { + let resolved = infcx.shallow_resolve(infer); + if resolved == infer { + // No progress. + return None; + } + + infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Infer(resolved), ..*constant }) + } + _ => constant, + }; + + let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; + wf.compute(constant.into()); + debug!("wf::const obligations({:?}, body_id={:?}) = {:?}", constant, body_id, wf.out); + + let result = wf.normalize(); + debug!("wf::const obligations({:?}, body_id={:?}) ~~> {:?}", constant, body_id, result); + Some(result) +} + /// Returns the obligations that make this trait reference /// well-formed. For example, if there is a trait `Set` defined like /// `trait Set`, then the trait reference `Foo: Set` is WF @@ -78,33 +108,36 @@ pub fn predicate_obligations<'a, 'tcx>( } ty::PredicateKind::RegionOutlives(..) => {} ty::PredicateKind::TypeOutlives(t) => { - wf.compute(t.skip_binder().0); + wf.compute(t.skip_binder().0.into()); } ty::PredicateKind::Projection(t) => { let t = t.skip_binder(); // (*) wf.compute_projection(t.projection_ty); - wf.compute(t.ty); + wf.compute(t.ty.into()); } &ty::PredicateKind::WellFormed(t) => { - wf.compute(t); + wf.compute(t.into()); } ty::PredicateKind::ObjectSafe(_) => {} ty::PredicateKind::ClosureKind(..) => {} ty::PredicateKind::Subtype(data) => { - wf.compute(data.skip_binder().a); // (*) - wf.compute(data.skip_binder().b); // (*) + wf.compute(data.skip_binder().a.into()); // (*) + wf.compute(data.skip_binder().b.into()); // (*) } &ty::PredicateKind::ConstEvaluatable(def_id, substs) => { let obligations = wf.nominal_obligations(def_id, substs); wf.out.extend(obligations); - for ty in substs.types() { - wf.compute(ty); + for subst in substs.iter().copied() { + wf.compute(subst); } } ty::PredicateKind::ConstEquate(c1, c2) => { - wf.compute(c1.ty); - wf.compute(c2.ty); + wf.compute(c1.ty.into()); + wf.compute(c2.ty.into()); + } + ty::Predicate::WellFormedConst(constant) => { + wf.compute(constant.into()); } } @@ -213,7 +246,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.infcx.tcx } - fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { + fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { traits::ObligationCause::new(self.span, self.body_id, code) } @@ -300,22 +333,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } } - /// Pushes the obligations required for an array length to be WF - /// into `self.out`. - fn compute_array_len(&mut self, constant: ty::Const<'tcx>) { - if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = constant.val { - assert!(promoted.is_none()); - - let obligations = self.nominal_obligations(def_id, substs); - self.out.extend(obligations); - - let predicate = - ty::PredicateKind::ConstEvaluatable(def_id, substs).to_predicate(self.tcx()); - let cause = self.cause(traits::MiscObligation); - self.out.push(traits::Obligation::new(cause, self.param_env, predicate)); - } - } - fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) { if !subty.has_escaping_bound_vars() { let cause = self.cause(cause); @@ -332,8 +349,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } /// Pushes all the predicates needed to validate that `ty` is WF into `out`. - fn compute(&mut self, ty: Ty<'tcx>) { - let mut walker = ty.walk(); + fn compute(&mut self, arg: GenericArg<'tcx>) { + let mut walker = arg.walk(); let param_env = self.param_env; while let Some(arg) = walker.next() { let ty = match arg.unpack() { @@ -343,9 +360,43 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // obligations are handled by the parent (e.g. `ty::Ref`). GenericArgKind::Lifetime(_) => continue, - // FIXME(eddyb) this is wrong and needs to be replaced - // (see https://github.com/rust-lang/rust/pull/70107). - GenericArgKind::Const(_) => continue, + GenericArgKind::Const(constant) => { + match constant.val { + ty::ConstKind::Unevaluated(def_id, substs, promoted) => { + assert!(promoted.is_none()); + + let obligations = self.nominal_obligations(def_id, substs); + self.out.extend(obligations); + + let predicate = ty::PredicateKind::ConstEvaluatable(def_id, substs).to_predicate(self.tcx()); + let cause = self.cause(traits::MiscObligation); + self.out.push(traits::Obligation::new( + cause, + self.param_env, + predicate, + )); + } + ty::ConstKind::Infer(infer) => { + let resolved = self.infcx.shallow_resolve(infer); + // the `InferConst` changed, meaning that we made progress. + if resolved != infer { + let cause = self.cause(traits::MiscObligation); + + let resolved_constant = self.infcx.tcx.mk_const(ty::Const { + val: ty::ConstKind::Infer(resolved), + ..*constant + }); + self.out.push(traits::Obligation::new( + cause, + self.param_env, + ty::PredicateKind::WellFormedConst(resolved_constant).to_predicate(self.tcx()), + )); + } + } + _ => (), + } + continue; + } }; match ty.kind { @@ -375,10 +426,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.require_sized(subty, traits::SliceOrArrayElem); } - ty::Array(subty, len) => { + ty::Array(subty, _) => { self.require_sized(subty, traits::SliceOrArrayElem); - // FIXME(eddyb) handle `GenericArgKind::Const` above instead. - self.compute_array_len(*len); } ty::Tuple(ref tys) => { @@ -467,7 +516,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { walker.skip_current_subtree(); // subtree handled below for upvar_ty in substs.as_closure().upvar_tys() { // FIXME(eddyb) add the type to `walker` instead of recursing. - self.compute(upvar_ty); + self.compute(upvar_ty.into()); } } @@ -540,7 +589,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } else { // Yes, resolved, proceed with the result. // FIXME(eddyb) add the type to `walker` instead of recursing. - self.compute(ty); + self.compute(ty.into()); } } }