Type check unnanotated constant items with NLL
This commit is contained in:
parent
65fe251634
commit
c76e55747b
3 changed files with 128 additions and 62 deletions
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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={:?})",
|
||||
|
|
@ -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(
|
||||
|
|
@ -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> {
|
||||
|
|
@ -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
|
||||
);
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue