Rollup merge of #58371 - davidtwco:issue-58299, r=arielb1

Check user type annotations for range patterns.

Fixes #58299.

This PR builds on the fix from #58161 (which fixed miscompilation
caused by the introduction of `AscribeUserType` patterns for associated
constants) to start checking these patterns are well-formed for ranges
(previous fix just ignored them so that miscompilation wouldn't occur).

r? @arielb1
This commit is contained in:
Mazdak Farrokhzad 2019-02-14 08:24:14 +01:00 committed by GitHub
commit 0dbb867beb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 176 additions and 85 deletions

View file

@ -7,7 +7,7 @@ use crate::build::scope::{CachedBlock, DropKind};
use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
use crate::hair::*;
use crate::hair::{self, *};
use rustc::mir::*;
use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty};
use rustc::ty::layout::VariantIdx;
@ -283,9 +283,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
},
..
},
user_ty: pat_ascription_ty,
variance: _,
user_ty_span,
ascription: hair::pattern::Ascription {
user_ty: pat_ascription_ty,
variance: _,
user_ty_span,
},
} => {
let place =
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard);
@ -560,9 +562,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
PatternKind::AscribeUserType {
ref subpattern,
ref user_ty,
user_ty_span,
variance: _,
ascription: hair::pattern::Ascription {
ref user_ty,
user_ty_span,
variance: _,
},
} => {
// This corresponds to something like
//

View file

@ -14,7 +14,7 @@
use crate::build::Builder;
use crate::build::matches::{Ascription, Binding, MatchPair, Candidate};
use crate::hair::*;
use crate::hair::{self, *};
use rustc::ty;
use rustc::ty::layout::{Integer, IntegerExt, Size};
use syntax::attr::{SignedInt, UnsignedInt};
@ -58,9 +58,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
match *match_pair.pattern.kind {
PatternKind::AscribeUserType {
ref subpattern,
variance,
ref user_ty,
user_ty_span
ascription: hair::pattern::Ascription {
variance,
ref user_ty,
user_ty_span,
},
} => {
// Apply the type ascription to the value at `match_pair.place`, which is the
// value being matched, taking the variance field into account.

View file

@ -1,4 +1,4 @@
use crate::hair::*;
use crate::hair::{self, *};
use crate::hair::cx::Cx;
use crate::hair::cx::to_ref::ToRef;
use rustc::middle::region;
@ -83,10 +83,12 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
ty: pattern.ty,
span: pattern.span,
kind: Box::new(PatternKind::AscribeUserType {
user_ty: PatternTypeProjection::from_user_type(user_ty),
user_ty_span: ty.span,
ascription: hair::pattern::Ascription {
user_ty: PatternTypeProjection::from_user_type(user_ty),
user_ty_span: ty.span,
variance: ty::Variance::Covariant,
},
subpattern: pattern,
variance: ty::Variance::Covariant,
})
};
}

View file

@ -871,18 +871,24 @@ impl<'tcx> IntRange<'tcx> {
}
fn from_pat(tcx: TyCtxt<'_, 'tcx, 'tcx>,
pat: &Pattern<'tcx>)
mut pat: &Pattern<'tcx>)
-> Option<IntRange<'tcx>> {
Self::from_ctor(tcx, &match pat.kind {
box PatternKind::Constant { value } => ConstantValue(value),
box PatternKind::Range(PatternRange { lo, hi, ty, end }) => ConstantRange(
lo.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
hi.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
ty,
end,
),
_ => return None,
})
let range = loop {
match pat.kind {
box PatternKind::Constant { value } => break ConstantValue(value),
box PatternKind::Range(PatternRange { lo, hi, ty, end }) => break ConstantRange(
lo.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
hi.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
ty,
end,
),
box PatternKind::AscribeUserType { ref subpattern, .. } => {
pat = subpattern;
},
_ => return None,
}
};
Self::from_ctor(tcx, &range)
}
// The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.

View file

@ -58,7 +58,7 @@ pub struct Pattern<'tcx> {
}
#[derive(Clone, Debug)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct PatternTypeProjection<'tcx> {
pub user_ty: CanonicalUserType<'tcx>,
}
@ -87,33 +87,38 @@ impl<'tcx> PatternTypeProjection<'tcx> {
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Ascription<'tcx> {
pub user_ty: PatternTypeProjection<'tcx>,
/// Variance to use when relating the type `user_ty` to the **type of the value being
/// matched**. Typically, this is `Variance::Covariant`, since the value being matched must
/// have a type that is some subtype of the ascribed type.
///
/// Note that this variance does not apply for any bindings within subpatterns. The type
/// assigned to those bindings must be exactly equal to the `user_ty` given here.
///
/// The only place where this field is not `Covariant` is when matching constants, where
/// we currently use `Contravariant` -- this is because the constant type just needs to
/// be "comparable" to the type of the input value. So, for example:
///
/// ```text
/// match x { "foo" => .. }
/// ```
///
/// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should
/// probably be checking for a `PartialEq` impl instead, but this preserves the behavior
/// of the old type-check for now. See #57280 for details.
pub variance: ty::Variance,
pub user_ty_span: Span,
}
#[derive(Clone, Debug)]
pub enum PatternKind<'tcx> {
Wild,
AscribeUserType {
user_ty: PatternTypeProjection<'tcx>,
ascription: Ascription<'tcx>,
subpattern: Pattern<'tcx>,
/// Variance to use when relating the type `user_ty` to the **type of the value being
/// matched**. Typically, this is `Variance::Covariant`, since the value being matched must
/// have a type that is some subtype of the ascribed type.
///
/// Note that this variance does not apply for any bindings within subpatterns. The type
/// assigned to those bindings must be exactly equal to the `user_ty` given here.
///
/// The only place where this field is not `Covariant` is when matching constants, where
/// we currently use `Contravariant` -- this is because the constant type just needs to
/// be "comparable" to the type of the input value. So, for example:
///
/// ```text
/// match x { "foo" => .. }
/// ```
///
/// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should
/// probably be checking for a `PartialEq` impl instead, but this preserves the behavior
/// of the old type-check for now. See #57280 for details.
variance: ty::Variance,
user_ty_span: Span,
},
/// `x`, `ref x`, `x @ P`, etc.
@ -169,18 +174,7 @@ pub enum PatternKind<'tcx> {
},
}
impl<'tcx> PatternKind<'tcx> {
/// If this is a `PatternKind::AscribeUserType` then return the subpattern kind, otherwise
/// return this pattern kind.
fn with_user_type_ascription_subpattern(self) -> Self {
match self {
PatternKind::AscribeUserType { subpattern: Pattern { box kind, .. }, .. } => kind,
kind => kind,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct PatternRange<'tcx> {
pub lo: ty::Const<'tcx>,
pub hi: ty::Const<'tcx>,
@ -407,6 +401,19 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
)
}
fn lower_range_expr(
&mut self,
expr: &'tcx hir::Expr,
) -> (PatternKind<'tcx>, Option<Ascription<'tcx>>) {
match self.lower_lit(expr) {
PatternKind::AscribeUserType {
ascription: lo_ascription,
subpattern: Pattern { kind: box kind, .. },
} => (kind, Some(lo_ascription)),
kind => (kind, None),
}
}
fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> {
let mut ty = self.tables.node_type(pat.hir_id);
@ -416,14 +423,10 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
PatKind::Lit(ref value) => self.lower_lit(value),
PatKind::Range(ref lo_expr, ref hi_expr, end) => {
match (
// Look for `PatternKind::Constant` patterns inside of any
// `PatternKind::AscribeUserType` patterns. Type ascriptions can be safely
// ignored for the purposes of lowering a range correctly - these are checked
// elsewhere for well-formedness.
self.lower_lit(lo_expr).with_user_type_ascription_subpattern(),
self.lower_lit(hi_expr).with_user_type_ascription_subpattern(),
) {
let (lo, lo_ascription) = self.lower_range_expr(lo_expr);
let (hi, hi_ascription) = self.lower_range_expr(hi_expr);
let mut kind = match (lo, hi) {
(PatternKind::Constant { value: lo }, PatternKind::Constant { value: hi }) => {
use std::cmp::Ordering;
let cmp = compare_const_vals(
@ -472,17 +475,33 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
PatternKind::Wild
}
}
}
},
ref pats => {
self.tcx.sess.delay_span_bug(
pat.span,
&format!("found bad range pattern `{:?}` outside of error recovery",
pats),
&format!(
"found bad range pattern `{:?}` outside of error recovery",
pats,
),
);
PatternKind::Wild
},
};
// If we are handling a range with associated constants (e.g.
// `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
// constants somewhere. Have them on the range pattern.
for ascription in &[lo_ascription, hi_ascription] {
if let Some(ascription) = ascription {
kind = PatternKind::AscribeUserType {
ascription: *ascription,
subpattern: Pattern { span: pat.span, ty, kind: Box::new(kind), },
};
}
}
kind
}
PatKind::Path(ref qpath) => {
@ -758,9 +777,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
ty,
kind: Box::new(kind),
},
user_ty: PatternTypeProjection::from_user_type(user_ty),
user_ty_span: span,
variance: ty::Variance::Covariant,
ascription: Ascription {
user_ty: PatternTypeProjection::from_user_type(user_ty),
user_ty_span: span,
variance: ty::Variance::Covariant,
},
};
}
@ -810,11 +831,13 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
kind: Box::new(
PatternKind::AscribeUserType {
subpattern: pattern,
/// Note that use `Contravariant` here. See the
/// `variance` field documentation for details.
variance: ty::Variance::Contravariant,
user_ty,
user_ty_span: span,
ascription: Ascription {
/// Note that use `Contravariant` here. See the
/// `variance` field documentation for details.
variance: ty::Variance::Contravariant,
user_ty,
user_ty_span: span,
},
}
),
ty: value.ty,
@ -1107,14 +1130,18 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
PatternKind::Wild => PatternKind::Wild,
PatternKind::AscribeUserType {
ref subpattern,
variance,
ref user_ty,
user_ty_span,
ascription: Ascription {
variance,
ref user_ty,
user_ty_span,
},
} => PatternKind::AscribeUserType {
subpattern: subpattern.fold_with(folder),
user_ty: user_ty.fold_with(folder),
variance,
user_ty_span,
ascription: Ascription {
user_ty: user_ty.fold_with(folder),
variance,
user_ty_span,
},
},
PatternKind::Binding {
mutability,

View file

@ -0,0 +1,30 @@
#![allow(dead_code)]
#![feature(nll)]
struct A<'a>(&'a ());
trait Y {
const X: i32;
}
impl Y for A<'static> {
const X: i32 = 10;
}
fn foo<'a>(x: i32) {
match x {
// This uses <A<'a> as Y>::X, but `A<'a>` does not implement `Y`.
A::<'a>::X..=A::<'static>::X => (), //~ ERROR lifetime may not live long enough
_ => (),
}
}
fn bar<'a>(x: i32) {
match x {
// This uses <A<'a> as Y>::X, but `A<'a>` does not implement `Y`.
A::<'static>::X..=A::<'a>::X => (), //~ ERROR lifetime may not live long enough
_ => (),
}
}
fn main() {}

View file

@ -0,0 +1,20 @@
error: lifetime may not live long enough
--> $DIR/issue-58299.rs:17:9
|
LL | fn foo<'a>(x: i32) {
| -- lifetime `'a` defined here
...
LL | A::<'a>::X..=A::<'static>::X => (), //~ ERROR lifetime may not live long enough
| ^^^^^^^^^^ requires that `'a` must outlive `'static`
error: lifetime may not live long enough
--> $DIR/issue-58299.rs:25:27
|
LL | fn bar<'a>(x: i32) {
| -- lifetime `'a` defined here
...
LL | A::<'static>::X..=A::<'a>::X => (), //~ ERROR lifetime may not live long enough
| ^^^^^^^^^^ requires that `'a` must outlive `'static`
error: aborting due to 2 previous errors