From f2532012dd51914f8cf27ba79cf0e4999761c9b4 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 22 Nov 2018 20:35:24 +0100 Subject: [PATCH] Always check well-formedness. This commit uses the map introduced by the previous commit to ensure that types are always checked for well-formedness by the NLL type check. Previously, without the map introduced by the previous commit, types would not be checked for well-formedness if the `AscribeUserType` statement that would trigger that check was removed as unreachable code. --- src/librustc/dep_graph/dep_node.rs | 4 +- src/librustc/traits/query/mod.rs | 4 + .../traits/query/type_op/ascribe_user_type.rs | 60 +++++++++- src/librustc/ty/query/config.rs | 12 +- src/librustc/ty/query/mod.rs | 11 +- src/librustc/ty/query/plumbing.rs | 1 + .../borrow_check/nll/type_check/mod.rs | 24 ++++ src/librustc_traits/type_op.rs | 106 +++++++++++++----- ...ns-free-region-ordering-caller1.nll.stderr | 19 +++- ...egions-outlives-projection-container-wc.rs | 4 +- ...ns-outlives-projection-container-wc.stderr | 6 +- 11 files changed, 209 insertions(+), 42 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index e5fd0aa3c9c..1f19e6fc689 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -62,7 +62,8 @@ use syntax_pos::symbol::InternedString; use traits; use traits::query::{ CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal, - CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal, CanonicalPredicateGoal, + CanonicalTypeOpAscribeUserTypeWellFormedGoal, CanonicalTypeOpEqGoal, + CanonicalTypeOpSubtypeGoal, CanonicalPredicateGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpNormalizeGoal, }; use ty::{TyCtxt, FnSig, Instance, InstanceDef, @@ -650,6 +651,7 @@ define_dep_nodes!( <'tcx> [] EvaluateObligation(CanonicalPredicateGoal<'tcx>), [] EvaluateGoal(traits::ChalkCanonicalGoal<'tcx>), [] TypeOpAscribeUserType(CanonicalTypeOpAscribeUserTypeGoal<'tcx>), + [] TypeOpAscribeUserTypeWellFormed(CanonicalTypeOpAscribeUserTypeWellFormedGoal<'tcx>), [] TypeOpEq(CanonicalTypeOpEqGoal<'tcx>), [] TypeOpSubtype(CanonicalTypeOpSubtypeGoal<'tcx>), [] TypeOpProvePredicate(CanonicalTypeOpProvePredicateGoal<'tcx>), diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index 59f786025b2..3203dc4e8cf 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -28,6 +28,10 @@ pub type CanonicalPredicateGoal<'tcx> = pub type CanonicalTypeOpAscribeUserTypeGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::ascribe_user_type::AscribeUserType<'tcx>>>; +pub type CanonicalTypeOpAscribeUserTypeWellFormedGoal<'tcx> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, + type_op::ascribe_user_type::AscribeUserTypeWellFormed<'tcx>>>; + pub type CanonicalTypeOpEqGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::eq::Eq<'tcx>>>; diff --git a/src/librustc/traits/query/type_op/ascribe_user_type.rs b/src/librustc/traits/query/type_op/ascribe_user_type.rs index 365c1d67ef9..993d09d2ed0 100644 --- a/src/librustc/traits/query/type_op/ascribe_user_type.rs +++ b/src/librustc/traits/query/type_op/ascribe_user_type.rs @@ -2,7 +2,7 @@ use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, Que use traits::query::Fallible; use hir::def_id::DefId; use mir::ProjectionKind; -use ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use ty::{self, ParamEnvAnd, Ty, TyCtxt, UserTypeAnnotation}; use ty::subst::UserSubsts; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] @@ -22,7 +22,7 @@ impl<'tcx> AscribeUserType<'tcx> { user_substs: UserSubsts<'tcx>, projs: &'tcx ty::List>, ) -> Self { - AscribeUserType { mir_ty, variance, def_id, user_substs, projs } + Self { mir_ty, variance, def_id, user_substs, projs } } } @@ -68,3 +68,59 @@ impl_stable_hash_for! { mir_ty, variance, def_id, user_substs, projs } } + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct AscribeUserTypeWellFormed<'tcx> { + pub user_type_annotation: UserTypeAnnotation<'tcx>, +} + +impl<'tcx> AscribeUserTypeWellFormed<'tcx> { + pub fn new( + user_type_annotation: UserTypeAnnotation<'tcx>, + ) -> Self { + Self { user_type_annotation, } + } +} + +impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for AscribeUserTypeWellFormed<'tcx> { + type QueryResponse = (); + + fn try_fast_path( + _tcx: TyCtxt<'_, 'gcx, 'tcx>, + _key: &ParamEnvAnd<'tcx, Self>, + ) -> Option { + None + } + + fn perform_query( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible> { + tcx.type_op_ascribe_user_type_well_formed(canonicalized) + } + + fn shrink_to_tcx_lifetime( + v: &'a CanonicalizedQueryResponse<'gcx, ()>, + ) -> &'a Canonical<'tcx, QueryResponse<'tcx, ()>> { + v + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for AscribeUserTypeWellFormed<'tcx> { + user_type_annotation + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for AscribeUserTypeWellFormed<'a> { + type Lifted = AscribeUserTypeWellFormed<'tcx>; + user_type_annotation + } +} + +impl_stable_hash_for! { + struct AscribeUserTypeWellFormed<'tcx> { + user_type_annotation + } +} diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs index 3464464aa22..bed4dfd97ca 100644 --- a/src/librustc/ty/query/config.rs +++ b/src/librustc/ty/query/config.rs @@ -5,7 +5,8 @@ use mir::interpret::GlobalId; use traits; use traits::query::{ CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, - CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpAscribeUserTypeWellFormedGoal, + CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, }; use ty::{self, ParamEnvAnd, Ty, TyCtxt}; @@ -124,6 +125,15 @@ impl<'tcx> QueryDescription<'tcx> for queries::type_op_ascribe_user_type<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::type_op_ascribe_user_type_well_formed<'tcx> { + fn describe( + _tcx: TyCtxt<'_, '_, '_>, + goal: CanonicalTypeOpAscribeUserTypeWellFormedGoal<'tcx>, + ) -> Cow<'static, str> { + format!("evaluating `type_op_ascribe_user_type_well_formed` `{:?}`", goal).into() + } +} + impl<'tcx> QueryDescription<'tcx> for queries::type_op_eq<'tcx> { fn describe(_tcx: TyCtxt<'_, '_, '_>, goal: CanonicalTypeOpEqGoal<'tcx>) -> Cow<'static, str> { format!("evaluating `type_op_eq` `{:?}`", goal).into() diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index 22bd1cd90a7..c2f208308b2 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -26,7 +26,8 @@ use session::config::OutputFilenames; use traits::{self, Vtable}; use traits::query::{ CanonicalPredicateGoal, CanonicalProjectionGoal, - CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, + CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal, + CanonicalTypeOpAscribeUserTypeWellFormedGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpNormalizeGoal, NoSolution, }; @@ -609,6 +610,14 @@ define_queries! { <'tcx> NoSolution, >, + /// Do not call this query directly: part of the `Eq` type-op + [] fn type_op_ascribe_user_type_well_formed: TypeOpAscribeUserTypeWellFormed( + CanonicalTypeOpAscribeUserTypeWellFormedGoal<'tcx> + ) -> Result< + Lrc>>, + NoSolution, + >, + /// Do not call this query directly: part of the `Eq` type-op [] fn type_op_eq: TypeOpEq( CanonicalTypeOpEqGoal<'tcx> diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 5d23ee0994a..cdb3cd3ffcb 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -1208,6 +1208,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::EvaluateObligation | DepKind::EvaluateGoal | DepKind::TypeOpAscribeUserType | + DepKind::TypeOpAscribeUserTypeWellFormed | DepKind::TypeOpEq | DepKind::TypeOpSubtype | DepKind::TypeOpProvePredicate | diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 8f8abe09810..51ade33f74c 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -916,6 +916,28 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ); } + /// Check that user type annotations are well formed. + fn check_user_type_annotations_are_well_formed(&mut self) { + for index in self.mir.user_type_annotations.indices() { + let (span, _) = &self.mir.user_type_annotations[index]; + let type_annotation = self.instantiated_type_annotations[&index]; + if let Err(terr) = self.fully_perform_op( + Locations::All(*span), + ConstraintCategory::Assignment, + self.param_env.and(type_op::ascribe_user_type::AscribeUserTypeWellFormed::new( + type_annotation, + )), + ) { + span_mirbug!( + self, + type_annotation, + "bad user type annotation: {:?}", + terr, + ); + } + } + } + /// Given some operation `op` that manipulates types, proves /// predicates, or otherwise uses the inference context, executes /// `op` and then executes all the further obligations that `op` @@ -2389,6 +2411,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.check_terminator(mir, block_data.terminator(), location); self.check_iscleanup(mir, block_data); } + + self.check_user_type_annotations_are_well_formed(); } fn normalize(&mut self, value: T, location: impl NormalizeLocation) -> T diff --git a/src/librustc_traits/type_op.rs b/src/librustc_traits/type_op.rs index b9ac0394306..6691a036ac2 100644 --- a/src/librustc_traits/type_op.rs +++ b/src/librustc_traits/type_op.rs @@ -4,7 +4,7 @@ use rustc::infer::InferCtxt; use rustc::hir::def_id::DefId; use rustc::mir::ProjectionKind; use rustc::mir::tcx::PlaceTy; -use rustc::traits::query::type_op::ascribe_user_type::AscribeUserType; +use rustc::traits::query::type_op::ascribe_user_type::{AscribeUserType, AscribeUserTypeWellFormed}; use rustc::traits::query::type_op::eq::Eq; use rustc::traits::query::type_op::normalize::Normalize; use rustc::traits::query::type_op::prove_predicate::ProvePredicate; @@ -17,6 +17,7 @@ use rustc::ty::query::Providers; use rustc::ty::subst::{Kind, Subst, UserSubsts, UserSelfTy}; use rustc::ty::{ FnSig, Lift, ParamEnv, ParamEnvAnd, PolyFnSig, Predicate, Ty, TyCtxt, TypeFoldable, Variance, + UserTypeAnnotation, }; use rustc_data_structures::sync::Lrc; use std::fmt; @@ -26,6 +27,7 @@ use syntax_pos::DUMMY_SP; crate fn provide(p: &mut Providers) { *p = Providers { type_op_ascribe_user_type, + type_op_ascribe_user_type_well_formed, type_op_eq, type_op_prove_predicate, type_op_subtype, @@ -48,7 +50,7 @@ fn type_op_ascribe_user_type<'tcx>( ) = key.into_parts(); debug!( - "type_op_user_type_relation: mir_ty={:?} variance={:?} def_id={:?} \ + "type_op_ascribe_user_type: mir_ty={:?} variance={:?} def_id={:?} \ user_substs={:?} projs={:?}", mir_ty, variance, def_id, user_substs, projs ); @@ -60,6 +62,28 @@ fn type_op_ascribe_user_type<'tcx>( }) } +fn type_op_ascribe_user_type_well_formed<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, AscribeUserTypeWellFormed<'tcx>>>, +) -> Result>>, NoSolution> { + tcx.infer_ctxt() + .enter_canonical_trait_query(&canonicalized, |infcx, fulfill_cx, key| { + let ( + param_env, AscribeUserTypeWellFormed { user_type_annotation } + ) = key.into_parts(); + + debug!( + "type_op_ascribe_user_type_well_formed: user_type_annotation={:?}", + user_type_annotation, + ); + + let mut cx = AscribeUserTypeCx { infcx, param_env, fulfill_cx }; + cx.well_formed(user_type_annotation)?; + + Ok(()) + }) +} + struct AscribeUserTypeCx<'me, 'gcx: 'tcx, 'tcx: 'me> { infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, param_env: ParamEnv<'tcx>, @@ -109,6 +133,56 @@ impl AscribeUserTypeCx<'me, 'gcx, 'tcx> { value.subst(self.tcx(), substs) } + fn well_formed( + &mut self, + type_annotation: UserTypeAnnotation<'tcx> + ) -> Result<(), NoSolution> { + match type_annotation { + UserTypeAnnotation::Ty(ty) => { + self.prove_predicate(Predicate::WellFormed(ty)); + Ok(()) + }, + UserTypeAnnotation::TypeOf(did, user_substs) => { + let UserSubsts { + user_self_ty, + substs, + } = user_substs; + + let ty = self.tcx().type_of(did); + let ty = self.subst(ty, substs); + debug!("relate_type_and_user_type: ty of def-id is {:?}", ty); + let ty = self.normalize(ty); + + if let Some(UserSelfTy { + impl_def_id, + self_ty, + }) = user_self_ty { + let impl_self_ty = self.tcx().type_of(impl_def_id); + let impl_self_ty = self.subst(impl_self_ty, &substs); + let impl_self_ty = self.normalize(impl_self_ty); + + self.relate(self_ty, Variance::Invariant, impl_self_ty)?; + + self.prove_predicate(Predicate::WellFormed(impl_self_ty)); + } + + // In addition to proving the predicates, we have to + // prove that `ty` is well-formed -- this is because + // the WF of `ty` is predicated on the substs being + // well-formed, and we haven't proven *that*. We don't + // want to prove the WF of types from `substs` directly because they + // haven't been normalized. + // + // FIXME(nmatsakis): Well, perhaps we should normalize + // them? This would only be relevant if some input + // type were ill-formed but did not appear in `ty`, + // which...could happen with normalization... + self.prove_predicate(Predicate::WellFormed(ty)); + Ok(()) + }, + } + } + fn relate_mir_and_user_ty( &mut self, mir_ty: Ty<'tcx>, @@ -118,7 +192,7 @@ impl AscribeUserTypeCx<'me, 'gcx, 'tcx> { projs: &[ProjectionKind<'tcx>], ) -> Result<(), NoSolution> { let UserSubsts { - user_self_ty, + user_self_ty: _, substs, } = user_substs; let tcx = self.tcx(); @@ -158,19 +232,6 @@ impl AscribeUserTypeCx<'me, 'gcx, 'tcx> { self.relate(mir_ty, variance, ty)?; } - if let Some(UserSelfTy { - impl_def_id, - self_ty, - }) = user_self_ty { - let impl_self_ty = self.tcx().type_of(impl_def_id); - let impl_self_ty = self.subst(impl_self_ty, &substs); - let impl_self_ty = self.normalize(impl_self_ty); - - self.relate(self_ty, Variance::Invariant, impl_self_ty)?; - - self.prove_predicate(Predicate::WellFormed(impl_self_ty)); - } - // Prove the predicates coming along with `def_id`. // // Also, normalize the `instantiated_predicates` @@ -184,19 +245,6 @@ impl AscribeUserTypeCx<'me, 'gcx, 'tcx> { self.prove_predicate(instantiated_predicate); } - // In addition to proving the predicates, we have to - // prove that `ty` is well-formed -- this is because - // the WF of `ty` is predicated on the substs being - // well-formed, and we haven't proven *that*. We don't - // want to prove the WF of types from `substs` directly because they - // haven't been normalized. - // - // FIXME(nmatsakis): Well, perhaps we should normalize - // them? This would only be relevant if some input - // type were ill-formed but did not appear in `ty`, - // which...could happen with normalization... - self.prove_predicate(Predicate::WellFormed(ty)); - Ok(()) } } diff --git a/src/test/ui/regions/regions-free-region-ordering-caller1.nll.stderr b/src/test/ui/regions/regions-free-region-ordering-caller1.nll.stderr index 92c21fcb4ae..abda7ec5e07 100644 --- a/src/test/ui/regions/regions-free-region-ordering-caller1.nll.stderr +++ b/src/test/ui/regions/regions-free-region-ordering-caller1.nll.stderr @@ -12,6 +12,21 @@ LL | let z: &'a & usize = &(&y); LL | } | - temporary value is freed at the end of this statement -error: aborting due to previous error +error[E0597]: `y` does not live long enough + --> $DIR/regions-free-region-ordering-caller1.rs:9:27 + | +LL | fn call1<'a>(x: &'a usize) { + | -- lifetime `'a` defined here +... +LL | let z: &'a & usize = &(&y); + | ----------- ^^^^ borrowed value does not live long enough + | | + | assignment requires that `y` is borrowed for `'a` +... +LL | } + | - `y` dropped here while still borrowed -For more information about this error, try `rustc --explain E0716`. +error: aborting due to 2 previous errors + +Some errors occurred: E0597, E0716. +For more information about an error, try `rustc --explain E0597`. diff --git a/src/test/ui/regions/regions-outlives-projection-container-wc.rs b/src/test/ui/regions/regions-outlives-projection-container-wc.rs index d38706defe7..91a0d8590ff 100644 --- a/src/test/ui/regions/regions-outlives-projection-container-wc.rs +++ b/src/test/ui/regions/regions-outlives-projection-container-wc.rs @@ -31,9 +31,7 @@ fn with_assoc<'a,'b>() { // outlive 'a. In this case, that means TheType<'b>::TheAssocType, // which is &'b (), must outlive 'a. - // FIXME (#54943) NLL doesn't enforce WF condition in unreachable code if - // `_x` is changed to `_` - let _x: &'a WithAssoc> = loop { }; + let _: &'a WithAssoc> = loop { }; //~^ ERROR reference has a longer lifetime } diff --git a/src/test/ui/regions/regions-outlives-projection-container-wc.stderr b/src/test/ui/regions/regions-outlives-projection-container-wc.stderr index 2ed9fd4f9b4..0d73d3d6432 100644 --- a/src/test/ui/regions/regions-outlives-projection-container-wc.stderr +++ b/src/test/ui/regions/regions-outlives-projection-container-wc.stderr @@ -1,8 +1,8 @@ error[E0491]: in type `&'a WithAssoc>`, reference has a longer lifetime than the data it references - --> $DIR/regions-outlives-projection-container-wc.rs:36:13 + --> $DIR/regions-outlives-projection-container-wc.rs:34:12 | -LL | let _x: &'a WithAssoc> = loop { }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: &'a WithAssoc> = loop { }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the pointer is valid for the lifetime 'a as defined on the function body at 28:15 --> $DIR/regions-outlives-projection-container-wc.rs:28:15