Skip to content

Commit

Permalink
Always check well-formedness.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
davidtwco committed Dec 30, 2018
1 parent 24a7a01 commit f253201
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 42 deletions.
4 changes: 3 additions & 1 deletion src/librustc/dep_graph/dep_node.rs
Expand Up @@ -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,
Expand Down Expand Up @@ -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>),
Expand Down
4 changes: 4 additions & 0 deletions src/librustc/traits/query/mod.rs
Expand Up @@ -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>>>;

Expand Down
60 changes: 58 additions & 2 deletions src/librustc/traits/query/type_op/ascribe_user_type.rs
Expand Up @@ -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)]
Expand All @@ -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 }
}
}

Expand Down Expand Up @@ -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
}
}
12 changes: 11 additions & 1 deletion src/librustc/ty/query/config.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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()
Expand Down
11 changes: 10 additions & 1 deletion src/librustc/ty/query/mod.rs
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/query/plumbing.rs
Expand Up @@ -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 |
Expand Down
24 changes: 24 additions & 0 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Expand Up @@ -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`
Expand Down Expand Up @@ -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
Expand Down
106 changes: 77 additions & 29 deletions src/librustc_traits/type_op.rs
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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,
Expand All @@ -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
);
Expand All @@ -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>,
Expand Down Expand Up @@ -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>,
Expand All @@ -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();
Expand Down Expand Up @@ -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`
Expand All @@ -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(())
}
}
Expand Down
Expand Up @@ -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`.

0 comments on commit f253201

Please sign in to comment.