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.
This commit is contained in:
David Wood 2018-11-22 20:35:24 +01:00
parent 24a7a010d1
commit f2532012dd
No known key found for this signature in database
GPG Key ID: 01760B4F9F53F154
11 changed files with 209 additions and 42 deletions

View File

@ -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>),

View File

@ -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>>>;

View File

@ -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<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 @@ 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<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
}
}

View File

@ -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()

View File

@ -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<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>

View File

@ -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 |

View File

@ -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<T>(&mut self, value: T, location: impl NormalizeLocation) -> T

View File

@ -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<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 @@ 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(())
}
}

View 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
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`.

View 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
}

View 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