Skip to content

Commit

Permalink
Type check unnanotated constant items with NLL
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjasper committed Jan 19, 2019
1 parent 65fe251 commit c76e557
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 62 deletions.
111 changes: 49 additions & 62 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Expand Up @@ -35,7 +35,7 @@ use rustc::traits::query::type_op::custom::CustomTypeOp;
use rustc::traits::query::{Fallible, NoSolution};
use rustc::traits::{ObligationCause, PredicateObligations};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::subst::{Subst, Substs, UnpackedKind};
use rustc::ty::subst::{Subst, Substs, UnpackedKind, UserSubsts};
use rustc::ty::{
self, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind, UserType,
CanonicalUserTypeAnnotation, UserTypeAnnotationIndex,
Expand Down Expand Up @@ -283,7 +283,7 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
location.to_locations(),
ConstraintCategory::Boring,
) {
let annotation = self.cx.instantiated_type_annotations[&annotation_index];
let annotation = &self.mir.user_type_annotations[annotation_index];
span_mirbug!(
self,
constant,
Expand All @@ -293,6 +293,39 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
terr,
);
}
} else {
match *constant.literal {
ty::LazyConst::Unevaluated(def_id, substs) => {
if let Err(terr) = self.cx.fully_perform_op(
location.to_locations(),
ConstraintCategory::Boring,
self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
constant.ty, def_id, UserSubsts { substs, user_self_ty: None },
)),
) {
span_mirbug!(
self,
constant,
"bad constant type {:?} ({:?})",
constant,
terr
);
}
}
ty::LazyConst::Evaluated(lit) => {
if let ty::FnDef(def_id, substs) = lit.ty.sty {
let tcx = self.tcx();

let instantiated_predicates = tcx
.predicates_of(def_id)
.instantiate(tcx, substs);
self.cx.normalize_and_prove_instantiated_predicates(
instantiated_predicates,
location.to_locations(),
);
}
}
}
}
}

Expand Down Expand Up @@ -374,8 +407,9 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
}
}

/// Checks that the constant's `ty` field matches up with what
/// would be expected from its literal.
/// Checks that the constant's `ty` field matches up with what would be
/// expected from its literal. Unevaluated constants and well-formed
/// constraints are checked by `visit_constant`.
fn sanitize_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
debug!(
"sanitize_constant(constant={:?}, location={:?})",
Expand All @@ -387,35 +421,6 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
ty::LazyConst::Unevaluated(..) => return,
};

// FIXME(#46702) -- We need some way to get the predicates
// associated with the "pre-evaluated" form of the
// constant. For example, consider that the constant
// may have associated constant projections (`<Foo as
// Trait<'a, 'b>>::SOME_CONST`) that impose
// constraints on `'a` and `'b`. These constraints
// would be lost if we just look at the normalized
// value.
if let ty::FnDef(def_id, substs) = literal.ty.sty {
let tcx = self.tcx();
let type_checker = &mut self.cx;

// FIXME -- For now, use the substitutions from
// `value.ty` rather than `value.val`. The
// renumberer will rewrite them to independent
// sets of regions; in principle, we ought to
// derive the type of the `value.val` from "first
// principles" and equate with value.ty, but as we
// are transitioning to the miri-based system, we
// don't have a handy function for that, so for
// now we just ignore `value.val` regions.

let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
type_checker.normalize_and_prove_instantiated_predicates(
instantiated_predicates,
location.to_locations(),
);
}

debug!("sanitize_constant: expected_ty={:?}", literal.ty);

if let Err(terr) = self.cx.eq_types(
Expand Down Expand Up @@ -740,15 +745,6 @@ struct TypeChecker<'a, 'gcx: 'tcx, 'tcx: 'a> {
reported_errors: FxHashSet<(Ty<'tcx>, Span)>,
borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>,
universal_region_relations: Option<&'a UniversalRegionRelations<'tcx>>,
/// For each user-type annotation (identified by a UserTypeAnnotationIndex), we create
/// an "instantiated" version at the beginning of type check, which replaces each
/// canonical variable with a fresh inference variable. These instantiated versions are
/// stored either in this field or in user_substs, depending on the kind of user-type
/// annotation. They are then referenced by the code which has the job of enforcing these
/// annotations. Part of the reason for this setup is that it allows us to enforce basic
/// WF criteria on the types even if the code that referenced them is dead
/// code (see #54943).
instantiated_type_annotations: FxHashMap<UserTypeAnnotationIndex, Ty<'tcx>>,
}

struct BorrowCheckContext<'a, 'tcx: 'a> {
Expand Down Expand Up @@ -905,23 +901,19 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
borrowck_context,
reported_errors: Default::default(),
universal_region_relations,
instantiated_type_annotations: Default::default(),
};
checker.instantiate_user_type_annotations();
checker.check_user_type_annotations();
checker
}

/// Instantiate canonical types from user type annotations in the `Mir` into the
/// `TypeChecker`. Used when relating user type annotations and when checking if
/// annotations are well-formed.
fn instantiate_user_type_annotations(&mut self) {
/// Equate the inferred type and the annotated type for user type annotations
fn check_user_type_annotations(&mut self) {
debug!(
"instantiate_user_type_annotations: user_type_annotations={:?}",
"check_user_type_annotations: user_type_annotations={:?}",
self.mir.user_type_annotations
);
for annotation_index in self.mir.user_type_annotations.indices() {
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } =
self.mir.user_type_annotations[annotation_index];
for user_annotation in &self.mir.user_type_annotations {
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
let (annotation, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars(
span, user_ty
);
Expand All @@ -937,7 +929,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
) {
span_mirbug!(
self,
self.mir.user_type_annotations[annotation_index],
user_annotation,
"bad user type ({:?} = {:?}): {:?}",
ty,
inferred_ty,
Expand All @@ -961,7 +953,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
) {
span_mirbug!(
self,
self.mir.user_type_annotations[annotation_index],
user_annotation,
"bad user type AscribeUserType({:?}, {:?} {:?}): {:?}",
inferred_ty,
def_id,
Expand All @@ -971,12 +963,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
},
}
self.instantiated_type_annotations.insert(annotation_index, inferred_ty);
}
debug!(
"instantiate_user_type_annotations: instantiated_type_annotations={:?}",
self.instantiated_type_annotations,
);
}

/// Given some operation `op` that manipulates types, proves
Expand Down Expand Up @@ -1108,7 +1095,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
a, v, user_ty, locations,
);

let annotated_type = self.instantiated_type_annotations[&user_ty.base];
let annotated_type = self.mir.user_type_annotations[user_ty.base].inferred_ty;
let mut curr_projected_ty = PlaceTy::from_ty(annotated_type);

let tcx = self.infcx.tcx;
Expand Down Expand Up @@ -1293,7 +1280,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
location.to_locations(),
ConstraintCategory::Boring,
) {
let annotation = self.instantiated_type_annotations[&annotation_index];
let annotation = &mir.user_type_annotations[annotation_index];
span_mirbug!(
self,
stmt,
Expand Down Expand Up @@ -1352,7 +1339,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
Locations::All(stmt.source_info.span),
ConstraintCategory::TypeAnnotation,
) {
let annotation = self.instantiated_type_annotations[&projection.base];
let annotation = &mir.user_type_annotations[projection.base];
span_mirbug!(
self,
stmt,
Expand Down
29 changes: 29 additions & 0 deletions src/test/ui/nll/user-annotations/constant-in-expr-inherent-2.rs
@@ -0,0 +1,29 @@
// Test that we still check constants are well-formed, even when we there's no
// type annotation to check.

#![feature(nll)]

const FUN: fn(&'static ()) = |_| {};
struct A;
impl A {
const ASSOCIATED_FUN: fn(&'static ()) = |_| {};
}

struct B<'a>(&'a ());
impl B<'static> {
const ALSO_ASSOCIATED_FUN: fn(&'static ()) = |_| {};
}

trait Z: 'static {
const TRAIT_ASSOCIATED_FUN: fn(&'static Self) = |_| ();
}

impl Z for () {}

fn main() {
let x = ();
FUN(&x); //~ ERROR `x` does not live long enough
A::ASSOCIATED_FUN(&x); //~ ERROR `x` does not live long enough
B::ALSO_ASSOCIATED_FUN(&x); //~ ERROR `x` does not live long enough
<_>::TRAIT_ASSOCIATED_FUN(&x); //~ ERROR `x` does not live long enough
}
@@ -0,0 +1,50 @@
error[E0597]: `x` does not live long enough
--> $DIR/constant-in-expr-inherent-2.rs:25:9
|
LL | FUN(&x); //~ ERROR `x` does not live long enough
| ----^^-
| | |
| | borrowed value does not live long enough
| argument requires that `x` is borrowed for `'static`
...
LL | }
| - `x` dropped here while still borrowed

error[E0597]: `x` does not live long enough
--> $DIR/constant-in-expr-inherent-2.rs:26:23
|
LL | A::ASSOCIATED_FUN(&x); //~ ERROR `x` does not live long enough
| ------------------^^-
| | |
| | borrowed value does not live long enough
| argument requires that `x` is borrowed for `'static`
...
LL | }
| - `x` dropped here while still borrowed

error[E0597]: `x` does not live long enough
--> $DIR/constant-in-expr-inherent-2.rs:27:28
|
LL | B::ALSO_ASSOCIATED_FUN(&x); //~ ERROR `x` does not live long enough
| -----------------------^^-
| | |
| | borrowed value does not live long enough
| argument requires that `x` is borrowed for `'static`
LL | <_>::TRAIT_ASSOCIATED_FUN(&x); //~ ERROR `x` does not live long enough
LL | }
| - `x` dropped here while still borrowed

error[E0597]: `x` does not live long enough
--> $DIR/constant-in-expr-inherent-2.rs:28:31
|
LL | <_>::TRAIT_ASSOCIATED_FUN(&x); //~ ERROR `x` does not live long enough
| --------------------------^^-
| | |
| | borrowed value does not live long enough
| argument requires that `x` is borrowed for `'static`
LL | }
| - `x` dropped here while still borrowed

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0597`.

0 comments on commit c76e557

Please sign in to comment.