Auto merge of #54757 - nikomatsakis:nll-issue-54573-user-annot, r=pnkfelix

user annotations in patterns

Fixes https://github.com/rust-lang/rust/issues/54573

r? @pnkfelix
This commit is contained in:
bors 2018-10-09 10:15:44 +00:00
commit e1643a8968
18 changed files with 387 additions and 77 deletions

View File

@ -214,7 +214,7 @@ macro_rules! make_mir_visitor {
self.super_ty(ty);
}
fn visit_canonical_ty(&mut self, ty: & $($mutability)* CanonicalTy<'tcx>) {
fn visit_user_ty(&mut self, ty: & $($mutability)* CanonicalTy<'tcx>) {
self.super_canonical_ty(ty);
}
@ -640,7 +640,7 @@ macro_rules! make_mir_visitor {
c_ty: & $($mutability)* CanonicalTy<'tcx>,
location: Location) {
self.visit_place(place, PlaceContext::Validate, location);
self.visit_canonical_ty(c_ty);
self.visit_user_ty(c_ty);
}
fn super_place(&mut self,
@ -736,7 +736,7 @@ macro_rules! make_mir_visitor {
source_info: *source_info,
});
if let Some(user_ty) = user_ty {
self.visit_canonical_ty(user_ty);
self.visit_user_ty(user_ty);
}
self.visit_source_info(source_info);
self.visit_source_scope(visibility_scope);

View File

@ -10,7 +10,7 @@
use rustc::ty::subst::Substs;
use rustc::ty::{self, CanonicalTy, ClosureSubsts, GeneratorSubsts, Ty, TypeFoldable};
use rustc::mir::{BasicBlock, Location, Mir, Place, Statement, StatementKind};
use rustc::mir::{BasicBlock, Location, Mir, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
@ -65,6 +65,14 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
debug!("visit_ty: ty={:?}", ty);
}
fn visit_user_ty(&mut self, _ty: &mut CanonicalTy<'tcx>) {
// `user_ty` annotations represent the types that the user
// wrote in the progarm. We don't want to erase the regions
// from these types: rather, we want to add them as
// constraints at type-check time.
debug!("visit_user_ty: skipping renumber");
}
fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
debug!("visit_substs(substs={:?}, location={:?})", substs, location);
@ -112,19 +120,6 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
debug!("visit_closure_substs: substs={:?}", substs);
}
fn visit_ascribe_user_ty(
&mut self,
_place: &mut Place<'tcx>,
_variance: &mut ty::Variance,
_c_ty: &mut CanonicalTy<'tcx>,
_location: Location,
) {
// User-assert-ty statements represent types that the user added explicitly.
// We don't want to erase the regions from these types: rather, we want to
// add them as constraints at type-check time.
debug!("visit_user_assert_ty: skipping renumber");
}
fn visit_statement(
&mut self,
block: BasicBlock,

View File

@ -1307,6 +1307,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
) {
for ascription in ascriptions {
let source_info = self.source_info(ascription.span);
debug!(
"adding user ascription at span {:?} of place {:?} and {:?}",
source_info.span,
ascription.source,
ascription.user_ty,
);
self.cfg.push(
block,
Statement {

View File

@ -13,6 +13,7 @@ use rustc_data_structures::indexed_vec::Idx;
use hair::cx::Cx;
use hair::cx::block;
use hair::cx::to_ref::ToRef;
use hair::util::UserAnnotatedTyHelpers;
use rustc::hir::def::{Def, CtorKind};
use rustc::mir::interpret::GlobalId;
use rustc::ty::{self, AdtKind, Ty};
@ -475,7 +476,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
adt_def: adt,
variant_index: 0,
substs,
user_ty: user_annotated_ty_for_adt(cx, expr.hir_id, adt),
user_ty: cx.user_substs_applied_to_adt(expr.hir_id, adt),
fields: field_refs(cx, fields),
base: base.as_ref().map(|base| {
FruInfo {
@ -501,7 +502,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
adt_def: adt,
variant_index: index,
substs,
user_ty: user_annotated_ty_for_adt(cx, expr.hir_id, adt),
user_ty: cx.user_substs_applied_to_adt(expr.hir_id, adt),
fields: field_refs(cx, fields),
base: None,
}
@ -787,48 +788,17 @@ fn user_annotated_ty_for_def(
// user.
Def::StructCtor(_def_id, CtorKind::Const) |
Def::VariantCtor(_def_id, CtorKind::Const) =>
match &cx.tables().node_id_to_type(hir_id).sty {
ty::Adt(adt_def, _) => user_annotated_ty_for_adt(cx, hir_id, adt_def),
sty => bug!("unexpected sty: {:?}", sty),
},
cx.user_substs_applied_to_ty_of_hir_id(hir_id),
// `Self` is used in expression as a tuple struct constructor or an unit struct constructor
Def::SelfCtor(_) => {
let sty = &cx.tables().node_id_to_type(hir_id).sty;
match sty {
ty::FnDef(ref def_id, _) => {
Some(cx.tables().user_substs(hir_id)?.unchecked_map(|user_substs| {
// Here, we just pair a `DefId` with the
// `user_substs`, so no new types etc are introduced.
cx.tcx().mk_fn_def(*def_id, user_substs)
}))
}
ty::Adt(ref adt_def, _) => {
user_annotated_ty_for_adt(cx, hir_id, adt_def)
}
_ => {
bug!("unexpected sty: {:?}", sty)
}
}
}
Def::SelfCtor(_) =>
cx.user_substs_applied_to_ty_of_hir_id(hir_id),
_ =>
bug!("user_annotated_ty_for_def: unexpected def {:?} at {:?}", def, hir_id)
}
}
fn user_annotated_ty_for_adt(
cx: &mut Cx<'a, 'gcx, 'tcx>,
hir_id: hir::HirId,
adt_def: &'tcx AdtDef,
) -> Option<CanonicalTy<'tcx>> {
let user_substs = cx.tables().user_substs(hir_id)?;
Some(user_substs.unchecked_map(|user_substs| {
// Here, we just pair an `AdtDef` with the
// `user_substs`, so no new types etc are introduced.
cx.tcx().mk_adt(adt_def, user_substs)
}))
}
fn method_callee<'a, 'gcx, 'tcx>(
cx: &mut Cx<'a, 'gcx, 'tcx>,
expr: &hir::Expr,
@ -943,7 +913,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
adt_def,
variant_index: adt_def.variant_index_with_id(def_id),
substs,
user_ty: user_annotated_ty_for_adt(cx, expr.hir_id, adt_def),
user_ty: cx.user_substs_applied_to_adt(expr.hir_id, adt_def),
fields: vec![],
base: None,
}

View File

@ -15,6 +15,7 @@
//!
use hair::*;
use hair::util::UserAnnotatedTyHelpers;
use rustc_data_structures::indexed_vec::Idx;
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
@ -272,6 +273,16 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
}
}
impl UserAnnotatedTyHelpers<'gcx, 'tcx> for Cx<'_, 'gcx, 'tcx> {
fn tcx(&self) -> TyCtxt<'_, 'gcx, 'tcx> {
self.tcx()
}
fn tables(&self) -> &ty::TypeckTables<'tcx> {
self.tables()
}
}
fn lint_level_for_hir_id(tcx: TyCtxt, mut id: ast::NodeId) -> ast::NodeId {
// Right now we insert a `with_ignore` node in the dep graph here to
// ignore the fact that `lint_levels` below depends on the entire crate.

View File

@ -29,6 +29,8 @@ pub mod cx;
pub mod pattern;
pub use self::pattern::{BindingMode, Pattern, PatternKind, FieldPattern};
mod util;
#[derive(Copy, Clone, Debug)]
pub enum LintLevel {
Inherited,

View File

@ -18,6 +18,8 @@ pub(crate) use self::check_match::check_match;
use const_eval::{const_field, const_variant_index};
use hair::util::UserAnnotatedTyHelpers;
use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability};
use rustc::mir::interpret::{Scalar, GlobalId, ConstValue, sign_extend};
use rustc::ty::{self, CanonicalTy, TyCtxt, AdtDef, Ty, Region};
@ -529,8 +531,9 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
field: Field::new(i),
pattern: self.lower_pattern(field),
})
.collect();
self.lower_variant_or_leaf(def, pat.span, ty, subpatterns)
.collect();
self.lower_variant_or_leaf(def, pat.hir_id, pat.span, ty, subpatterns)
}
PatKind::Struct(ref qpath, ref fields, _) => {
@ -546,7 +549,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
})
.collect();
self.lower_variant_or_leaf(def, pat.span, ty, subpatterns)
self.lower_variant_or_leaf(def, pat.hir_id, pat.span, ty, subpatterns)
}
};
@ -637,12 +640,12 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
fn lower_variant_or_leaf(
&mut self,
def: Def,
hir_id: hir::HirId,
span: Span,
ty: Ty<'tcx>,
subpatterns: Vec<FieldPattern<'tcx>>)
-> PatternKind<'tcx>
{
match def {
subpatterns: Vec<FieldPattern<'tcx>>,
) -> PatternKind<'tcx> {
let mut kind = match def {
Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => {
let enum_id = self.tcx.parent_def_id(variant_id).unwrap();
let adt_def = self.tcx.adt_def(enum_id);
@ -675,7 +678,24 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
self.errors.push(PatternError::NonConstPath(span));
PatternKind::Wild
}
};
if let Some(user_ty) = self.user_substs_applied_to_ty_of_hir_id(hir_id) {
let subpattern = Pattern {
span,
ty,
kind: Box::new(kind),
};
debug!("pattern user_ty = {:?} for pattern at {:?}", user_ty, span);
kind = PatternKind::AscribeUserType {
subpattern,
user_ty,
};
}
kind
}
/// Takes a HIR Path. If the path is a constant, evaluates it and feeds
@ -729,7 +749,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
},
}
}
_ => self.lower_variant_or_leaf(def, span, ty, vec![]),
_ => self.lower_variant_or_leaf(def, id, span, ty, vec![]),
};
Pattern {
@ -894,6 +914,17 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
}
}
impl UserAnnotatedTyHelpers<'tcx, 'tcx> for PatternContext<'_, 'tcx> {
fn tcx(&self) -> TyCtxt<'_, 'tcx, 'tcx> {
self.tcx
}
fn tables(&self) -> &ty::TypeckTables<'tcx> {
self.tables
}
}
pub trait PatternFoldable<'tcx> : Sized {
fn fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
self.super_fold_with(folder)

View File

@ -0,0 +1,56 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::hir;
use rustc::ty::{self, AdtDef, CanonicalTy, TyCtxt};
crate trait UserAnnotatedTyHelpers<'gcx: 'tcx, 'tcx> {
fn tcx(&self) -> TyCtxt<'_, 'gcx, 'tcx>;
fn tables(&self) -> &ty::TypeckTables<'tcx>;
fn user_substs_applied_to_adt(
&self,
hir_id: hir::HirId,
adt_def: &'tcx AdtDef,
) -> Option<CanonicalTy<'tcx>> {
let user_substs = self.tables().user_substs(hir_id)?;
Some(user_substs.unchecked_map(|user_substs| {
// Here, we just pair an `AdtDef` with the
// `user_substs`, so no new types etc are introduced.
self.tcx().mk_adt(adt_def, user_substs)
}))
}
/// Looks up the type associated with this hir-id and applies the
/// user-given substitutions; the hir-id must map to a suitable
/// type.
fn user_substs_applied_to_ty_of_hir_id(&self, hir_id: hir::HirId) -> Option<CanonicalTy<'tcx>> {
let user_substs = self.tables().user_substs(hir_id)?;
match &self.tables().node_id_to_type(hir_id).sty {
ty::Adt(adt_def, _) => Some(user_substs.unchecked_map(|user_substs| {
// Ok to call `unchecked_map` because we just pair an
// `AdtDef` with the `user_substs`, so no new types
// etc are introduced.
self.tcx().mk_adt(adt_def, user_substs)
})),
ty::FnDef(def_id, _) => Some(user_substs.unchecked_map(|user_substs| {
// Here, we just pair a `DefId` with the
// `user_substs`, so no new types etc are introduced.
self.tcx().mk_fn_def(*def_id, user_substs)
})),
sty => bug!(
"sty: {:?} should not have user-substs {:?} recorded ",
sty,
user_substs
),
}
}
}

View File

@ -402,21 +402,44 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
}
/// Creates the relevant generic argument substitutions
/// corresponding to a set of generic parameters.
pub fn create_substs_for_generic_args<'a, 'b, A, P, I>(
/// corresponding to a set of generic parameters. This is a
/// rather complex little function. Let me try to explain the
/// role of each of its parameters:
///
/// To start, we are given the `def_id` of the thing we are
/// creating the substitutions for, and a partial set of
/// substitutions `parent_substs`. In general, the substitutions
/// for an item begin with substitutions for all the "parents" of
/// that item -- so e.g. for a method it might include the
/// parameters from the impl.
///
/// Therefore, the method begins by walking down these parents,
/// starting with the outermost parent and proceed inwards until
/// it reaches `def_id`. For each parent P, it will check `parent_substs`
/// first to see if the parent's substitutions are listed in there. If so,
/// we can append those and move on. Otherwise, it invokes the
/// three callback functions:
///
/// - `args_for_def_id`: given the def-id P, supplies back the
/// generic arguments that were given to that parent from within
/// the path; so e.g. if you have `<T as Foo>::Bar`, the def-id
/// might refer to the trait `Foo`, and the arguments might be
/// `[T]`. The boolean value indicates whether to infer values
/// for arguments whose values were not explicitly provided.
/// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`,
/// instantiate a `Kind`
/// - `inferred_kind`: if no parameter was provided, and inference is enabled, then
/// creates a suitable inference variable.
pub fn create_substs_for_generic_args<'a, 'b>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
def_id: DefId,
parent_substs: &[Kind<'tcx>],
has_self: bool,
self_ty: Option<Ty<'tcx>>,
args_for_def_id: A,
provided_kind: P,
inferred_kind: I,
) -> &'tcx Substs<'tcx> where
A: Fn(DefId) -> (Option<&'b GenericArgs>, bool),
P: Fn(&GenericParamDef, &GenericArg) -> Kind<'tcx>,
I: Fn(Option<&[Kind<'tcx>]>, &GenericParamDef, bool) -> Kind<'tcx>
{
args_for_def_id: impl Fn(DefId) -> (Option<&'b GenericArgs>, bool),
provided_kind: impl Fn(&GenericParamDef, &GenericArg) -> Kind<'tcx>,
inferred_kind: impl Fn(Option<&[Kind<'tcx>]>, &GenericParamDef, bool) -> Kind<'tcx>,
) -> &'tcx Substs<'tcx> {
// Collect the segments of the path: we need to substitute arguments
// for parameters throughout the entire path (wherever there are
// generic parameters).

View File

@ -2164,6 +2164,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
/// occurred**, so that annotations like `Vec<_>` are preserved
/// properly.
pub fn write_user_substs_from_substs(&self, hir_id: hir::HirId, substs: &'tcx Substs<'tcx>) {
debug!(
"write_user_substs_from_substs({:?}, {:?}) in fcx {}",
hir_id,
substs,
self.tag(),
);
if !substs.is_noop() {
let user_substs = self.infcx.canonicalize_response(&substs);
debug!("instantiate_value_path: user_substs = {:?}", user_substs);
@ -3752,6 +3759,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expected: Expectation<'tcx>,
needs: Needs
) -> Ty<'tcx> {
debug!(
"check_expr_kind(expr={:?}, expected={:?}, needs={:?})",
expr,
expected,
needs,
);
let tcx = self.tcx;
let id = expr.id;
match expr.node {
@ -4981,10 +4995,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
span: Span,
node_id: ast::NodeId)
-> (Ty<'tcx>, Def) {
debug!("instantiate_value_path(path={:?}, def={:?}, node_id={})",
segments,
def,
node_id);
debug!(
"instantiate_value_path(segments={:?}, self_ty={:?}, def={:?}, node_id={})",
segments,
self_ty,
def,
node_id,
);
let path_segs = self.def_ids_for_path_segments(segments, def);
@ -5194,6 +5211,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let hir_id = self.tcx.hir.node_to_hir_id(node_id);
self.write_substs(hir_id, substs);
debug!(
"instantiate_value_path: id={:?} substs={:?}",
node_id,
substs,
);
self.write_user_substs_from_substs(hir_id, substs);
(ty_substituted, new_def)

View File

@ -0,0 +1,24 @@
#![feature(nll)]
enum Foo<'a> {
Bar { field: &'a u32 }
}
fn in_let() {
let y = 22;
let foo = Foo::Bar { field: &y };
//~^ ERROR `y` does not live long enough
let Foo::Bar::<'static> { field: _z } = foo;
}
fn in_match() {
let y = 22;
let foo = Foo::Bar { field: &y };
//~^ ERROR `y` does not live long enough
match foo {
Foo::Bar::<'static> { field: _z } => {
}
}
}
fn main() { }

View File

@ -0,0 +1,25 @@
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_brace_enum_variant.rs:9:33
|
LL | let foo = Foo::Bar { field: &y };
| ^^ borrowed value does not live long enough
...
LL | }
| - `y` dropped here while still borrowed
|
= note: borrowed value must be valid for the static lifetime...
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_brace_enum_variant.rs:16:33
|
LL | let foo = Foo::Bar { field: &y };
| ^^ borrowed value does not live long enough
...
LL | }
| - `y` dropped here while still borrowed
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0597`.

View File

@ -0,0 +1,22 @@
#![feature(nll)]
struct Foo<'a> { field: &'a u32 }
fn in_let() {
let y = 22;
let foo = Foo { field: &y };
//~^ ERROR `y` does not live long enough
let Foo::<'static> { field: _z } = foo;
}
fn in_main() {
let y = 22;
let foo = Foo { field: &y };
//~^ ERROR `y` does not live long enough
match foo {
Foo::<'static> { field: _z } => {
}
}
}
fn main() { }

View File

@ -0,0 +1,25 @@
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_brace_struct.rs:7:28
|
LL | let foo = Foo { field: &y };
| ^^ borrowed value does not live long enough
...
LL | }
| - `y` dropped here while still borrowed
|
= note: borrowed value must be valid for the static lifetime...
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_brace_struct.rs:14:28
|
LL | let foo = Foo { field: &y };
| ^^ borrowed value does not live long enough
...
LL | }
| - `y` dropped here while still borrowed
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0597`.

View File

@ -0,0 +1,24 @@
#![feature(nll)]
enum Foo<'a> {
Bar(&'a u32)
}
fn in_let() {
let y = 22;
let foo = Foo::Bar(&y);
//~^ ERROR `y` does not live long enough
let Foo::Bar::<'static>(_z) = foo;
}
fn in_match() {
let y = 22;
let foo = Foo::Bar(&y);
//~^ ERROR `y` does not live long enough
match foo {
Foo::Bar::<'static>(_z) => {
}
}
}
fn main() { }

View File

@ -0,0 +1,25 @@
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_tuple_enum_variant.rs:9:24
|
LL | let foo = Foo::Bar(&y);
| ^^ borrowed value does not live long enough
...
LL | }
| - `y` dropped here while still borrowed
|
= note: borrowed value must be valid for the static lifetime...
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_tuple_enum_variant.rs:16:24
|
LL | let foo = Foo::Bar(&y);
| ^^ borrowed value does not live long enough
...
LL | }
| - `y` dropped here while still borrowed
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0597`.

View File

@ -0,0 +1,22 @@
#![feature(nll)]
struct Foo<'a>(&'a u32);
fn in_let() {
let y = 22;
let foo = Foo(&y);
//~^ ERROR `y` does not live long enough
let Foo::<'static>(_z) = foo;
}
fn in_match() {
let y = 22;
let foo = Foo(&y);
//~^ ERROR `y` does not live long enough
match foo {
Foo::<'static>(_z) => {
}
}
}
fn main() { }

View File

@ -0,0 +1,25 @@
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_tuple_struct.rs:7:19
|
LL | let foo = Foo(&y);
| ^^ borrowed value does not live long enough
...
LL | }
| - `y` dropped here while still borrowed
|
= note: borrowed value must be valid for the static lifetime...
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_tuple_struct.rs:14:19
|
LL | let foo = Foo(&y);
| ^^ borrowed value does not live long enough
...
LL | }
| - `y` dropped here while still borrowed
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0597`.