]> git.lizzy.rs Git - rust.git/commitdiff
Always check well-formedness.
authorDavid Wood <david@davidtw.co>
Thu, 22 Nov 2018 19:35:24 +0000 (20:35 +0100)
committerDavid Wood <david@davidtw.co>
Sun, 30 Dec 2018 13:25:25 +0000 (14:25 +0100)
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
src/librustc/traits/query/mod.rs
src/librustc/traits/query/type_op/ascribe_user_type.rs
src/librustc/ty/query/config.rs
src/librustc/ty/query/mod.rs
src/librustc/ty/query/plumbing.rs
src/librustc_mir/borrow_check/nll/type_check/mod.rs
src/librustc_traits/type_op.rs
src/test/ui/regions/regions-free-region-ordering-caller1.nll.stderr
src/test/ui/regions/regions-outlives-projection-container-wc.rs
src/test/ui/regions/regions-outlives-projection-container-wc.stderr

index e5fd0aa3c9cbd8d8af9d26d7a854d1504048c0d4..1f19e6fc689c1aa2f25ef26d8b71af416ac2ac45 100644 (file)
@@ -62,7 +62,8 @@
 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 @@ pub fn fingerprint_needed_for_crate_hash(self) -> bool {
     [] EvaluateObligation(CanonicalPredicateGoal<'tcx>),
     [] EvaluateGoal(traits::ChalkCanonicalGoal<'tcx>),
     [] TypeOpAscribeUserType(CanonicalTypeOpAscribeUserTypeGoal<'tcx>),
+    [] TypeOpAscribeUserTypeWellFormed(CanonicalTypeOpAscribeUserTypeWellFormedGoal<'tcx>),
     [] TypeOpEq(CanonicalTypeOpEqGoal<'tcx>),
     [] TypeOpSubtype(CanonicalTypeOpSubtypeGoal<'tcx>),
     [] TypeOpProvePredicate(CanonicalTypeOpProvePredicateGoal<'tcx>),
index 59f786025b22485d75fe5b42088ae8d69c4b6b3d..3203dc4e8cf82bde25122509df020f63576bb2b1 100644 (file)
 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>>>;
 
index 365c1d67ef97a007f2962739f0d34df3e64b94dc..993d09d2ed02d8b2bb938959c0f904305658ebda 100644 (file)
@@ -2,7 +2,7 @@
 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 @@ pub fn new(
         user_substs: UserSubsts<'tcx>,
         projs: &'tcx ty::List<ProjectionKind<'tcx>>,
     ) -> Self {
-        AscribeUserType { mir_ty, variance, def_id, user_substs, projs }
+        Self { mir_ty, variance, def_id, user_substs, projs }
     }
 }
 
@@ -68,3 +68,59 @@ struct AscribeUserType<'tcx> {
         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<Self::QueryResponse> {
+        None
+    }
+
+    fn perform_query(
+        tcx: TyCtxt<'_, 'gcx, 'tcx>,
+        canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>,
+    ) -> Fallible<CanonicalizedQueryResponse<'gcx, ()>> {
+        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
+    }
+}
index 3464464aa229c9cc77dd5f0b47c26318dffd763b..bed4dfd97ca4949dc851177965703fd60d7bd38e 100644 (file)
@@ -5,7 +5,8 @@
 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 @@ fn describe(
     }
 }
 
+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()
index 22bd1cd90a754de1844fa041b7f253c6536bfec9..c2f208308b2d1f27e2780eb3203779371e460791 100644 (file)
@@ -26,7 +26,8 @@
 use traits::{self, Vtable};
 use traits::query::{
     CanonicalPredicateGoal, CanonicalProjectionGoal,
-    CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal,
+    CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal,
+    CanonicalTypeOpAscribeUserTypeWellFormedGoal, CanonicalTypeOpEqGoal,
     CanonicalTypeOpSubtypeGoal, CanonicalTypeOpProvePredicateGoal,
     CanonicalTypeOpNormalizeGoal, NoSolution,
 };
             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<Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>>,
+            NoSolution,
+        >,
+
         /// Do not call this query directly: part of the `Eq` type-op
         [] fn type_op_eq: TypeOpEq(
             CanonicalTypeOpEqGoal<'tcx>
index 5d23ee0994a06ac8c1430c883c71c975dad68ad6..cdb3cd3ffcb6b345d80616630188ec031f8e5bb3 100644 (file)
@@ -1208,6 +1208,7 @@ macro_rules! force {
         DepKind::EvaluateObligation |
         DepKind::EvaluateGoal |
         DepKind::TypeOpAscribeUserType |
+        DepKind::TypeOpAscribeUserTypeWellFormed |
         DepKind::TypeOpEq |
         DepKind::TypeOpSubtype |
         DepKind::TypeOpProvePredicate |
index 8f8abe09810a26143c4d8961aa7401ed5fa392c5..51ade33f74cf0102fe6affa0212a959c6492497f 100644 (file)
@@ -916,6 +916,28 @@ fn instantiate_user_type_annotations(&mut self) {
         );
     }
 
+    /// 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 @@ fn typeck_mir(&mut self, mir: &Mir<'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<T>(&mut self, value: T, location: impl NormalizeLocation) -> T
index b9ac0394306bec2867bf0fb85c073c0f9049b3ff..6691a036ac28c9068393c884ccde70f180786a82 100644 (file)
@@ -4,7 +4,7 @@
 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::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 @@
 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<Lrc<Canonical<'tcx, QueryResponse<'tcx, ()>>>, 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 @@ fn subst<T>(&self, value: T, substs: &[Kind<'tcx>]) -> T
         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 @@ fn relate_mir_and_user_ty(
         projs: &[ProjectionKind<'tcx>],
     ) -> Result<(), NoSolution> {
         let UserSubsts {
-            user_self_ty,
+            user_self_ty: _,
             substs,
         } = user_substs;
         let tcx = self.tcx();
@@ -158,19 +232,6 @@ fn relate_mir_and_user_ty(
             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 @@ fn relate_mir_and_user_ty(
             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(())
     }
 }
index 92c21fcb4aec5196f7cf1248674969b794bb39aa..abda7ec5e07a60389343a44b15efb97627e2398a 100644 (file)
@@ -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
+
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0716`.
+Some errors occurred: E0597, E0716.
+For more information about an error, try `rustc --explain E0597`.
index d38706defe7adfe4282af43a91ce7536e96d26a3..91a0d8590ff341e92ff00efaeda65f9dc511f717 100644 (file)
@@ -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<TheType<'b>> = loop { };
+    let _: &'a WithAssoc<TheType<'b>> = loop { };
     //~^ ERROR reference has a longer lifetime
 }
 
index 2ed9fd4f9b4cf5ada04e579624cf29203ae5c9be..0d73d3d64322e0e69e0631d7cb7930819a4dac24 100644 (file)
@@ -1,8 +1,8 @@
 error[E0491]: in type `&'a WithAssoc<TheType<'b>>`, 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<TheType<'b>> = loop { };
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let _: &'a WithAssoc<TheType<'b>> = 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