Check user type annotations for range patterns.

This commit 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).
This commit is contained in:
David Wood 2019-02-11 12:18:40 +01:00
parent 2e08bb1dd2
commit ee82d09b6c
No known key found for this signature in database
GPG Key ID: 01760B4F9F53F154
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::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard};
use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode}; use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
use crate::hair::*; use crate::hair::{self, *};
use rustc::mir::*; use rustc::mir::*;
use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty}; use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty};
use rustc::ty::layout::VariantIdx; use rustc::ty::layout::VariantIdx;
@ -283,9 +283,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}, },
.. ..
}, },
user_ty: pat_ascription_ty, ascription: hair::pattern::Ascription {
variance: _, user_ty: pat_ascription_ty,
user_ty_span, variance: _,
user_ty_span,
},
} => { } => {
let place = let place =
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard); self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard);
@ -560,9 +562,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} }
PatternKind::AscribeUserType { PatternKind::AscribeUserType {
ref subpattern, ref subpattern,
ref user_ty, ascription: hair::pattern::Ascription {
user_ty_span, ref user_ty,
variance: _, user_ty_span,
variance: _,
},
} => { } => {
// This corresponds to something like // This corresponds to something like
// //

View File

@ -14,7 +14,7 @@
use crate::build::Builder; use crate::build::Builder;
use crate::build::matches::{Ascription, Binding, MatchPair, Candidate}; use crate::build::matches::{Ascription, Binding, MatchPair, Candidate};
use crate::hair::*; use crate::hair::{self, *};
use rustc::ty; use rustc::ty;
use rustc::ty::layout::{Integer, IntegerExt, Size}; use rustc::ty::layout::{Integer, IntegerExt, Size};
use syntax::attr::{SignedInt, UnsignedInt}; use syntax::attr::{SignedInt, UnsignedInt};
@ -58,9 +58,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
match *match_pair.pattern.kind { match *match_pair.pattern.kind {
PatternKind::AscribeUserType { PatternKind::AscribeUserType {
ref subpattern, ref subpattern,
variance, ascription: hair::pattern::Ascription {
ref user_ty, variance,
user_ty_span ref user_ty,
user_ty_span,
},
} => { } => {
// Apply the type ascription to the value at `match_pair.place`, which is the // Apply the type ascription to the value at `match_pair.place`, which is the
// value being matched, taking the variance field into account. // 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::Cx;
use crate::hair::cx::to_ref::ToRef; use crate::hair::cx::to_ref::ToRef;
use rustc::middle::region; use rustc::middle::region;
@ -83,10 +83,12 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
ty: pattern.ty, ty: pattern.ty,
span: pattern.span, span: pattern.span,
kind: Box::new(PatternKind::AscribeUserType { kind: Box::new(PatternKind::AscribeUserType {
user_ty: PatternTypeProjection::from_user_type(user_ty), ascription: hair::pattern::Ascription {
user_ty_span: ty.span, user_ty: PatternTypeProjection::from_user_type(user_ty),
user_ty_span: ty.span,
variance: ty::Variance::Covariant,
},
subpattern: pattern, subpattern: pattern,
variance: ty::Variance::Covariant,
}) })
}; };
} }

View File

@ -871,18 +871,24 @@ impl<'tcx> IntRange<'tcx> {
} }
fn from_pat(tcx: TyCtxt<'_, 'tcx, 'tcx>, fn from_pat(tcx: TyCtxt<'_, 'tcx, 'tcx>,
pat: &Pattern<'tcx>) mut pat: &Pattern<'tcx>)
-> Option<IntRange<'tcx>> { -> Option<IntRange<'tcx>> {
Self::from_ctor(tcx, &match pat.kind { let range = loop {
box PatternKind::Constant { value } => ConstantValue(value), match pat.kind {
box PatternKind::Range(PatternRange { lo, hi, ty, end }) => ConstantRange( box PatternKind::Constant { value } => break ConstantValue(value),
lo.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(), box PatternKind::Range(PatternRange { lo, hi, ty, end }) => break ConstantRange(
hi.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(), lo.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
ty, hi.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
end, ty,
), end,
_ => return None, ),
}) 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. // 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 struct PatternTypeProjection<'tcx> {
pub user_ty: CanonicalUserType<'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)] #[derive(Clone, Debug)]
pub enum PatternKind<'tcx> { pub enum PatternKind<'tcx> {
Wild, Wild,
AscribeUserType { AscribeUserType {
user_ty: PatternTypeProjection<'tcx>, ascription: Ascription<'tcx>,
subpattern: Pattern<'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 /// x, ref x, x @ P, etc
@ -167,18 +172,7 @@ pub enum PatternKind<'tcx> {
}, },
} }
impl<'tcx> PatternKind<'tcx> { #[derive(Copy, Clone, Debug, PartialEq)]
/// 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)]
pub struct PatternRange<'tcx> { pub struct PatternRange<'tcx> {
pub lo: ty::Const<'tcx>, pub lo: ty::Const<'tcx>,
pub hi: ty::Const<'tcx>, pub hi: ty::Const<'tcx>,
@ -405,6 +399,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> { fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> {
let mut ty = self.tables.node_id_to_type(pat.hir_id); let mut ty = self.tables.node_id_to_type(pat.hir_id);
@ -414,14 +421,10 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
PatKind::Lit(ref value) => self.lower_lit(value), PatKind::Lit(ref value) => self.lower_lit(value),
PatKind::Range(ref lo_expr, ref hi_expr, end) => { PatKind::Range(ref lo_expr, ref hi_expr, end) => {
match ( let (lo, lo_ascription) = self.lower_range_expr(lo_expr);
// Look for `PatternKind::Constant` patterns inside of any let (hi, hi_ascription) = self.lower_range_expr(hi_expr);
// `PatternKind::AscribeUserType` patterns. Type ascriptions can be safely
// ignored for the purposes of lowering a range correctly - these are checked let mut kind = match (lo, hi) {
// elsewhere for well-formedness.
self.lower_lit(lo_expr).with_user_type_ascription_subpattern(),
self.lower_lit(hi_expr).with_user_type_ascription_subpattern(),
) {
(PatternKind::Constant { value: lo }, PatternKind::Constant { value: hi }) => { (PatternKind::Constant { value: lo }, PatternKind::Constant { value: hi }) => {
use std::cmp::Ordering; use std::cmp::Ordering;
let cmp = compare_const_vals( let cmp = compare_const_vals(
@ -470,17 +473,33 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
PatternKind::Wild PatternKind::Wild
} }
} }
} },
ref pats => { ref pats => {
self.tcx.sess.delay_span_bug( self.tcx.sess.delay_span_bug(
pat.span, pat.span,
&format!("found bad range pattern `{:?}` outside of error recovery", &format!(
pats), "found bad range pattern `{:?}` outside of error recovery",
pats,
),
); );
PatternKind::Wild 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) => { PatKind::Path(ref qpath) => {
@ -756,9 +775,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
ty, ty,
kind: Box::new(kind), kind: Box::new(kind),
}, },
user_ty: PatternTypeProjection::from_user_type(user_ty), ascription: Ascription {
user_ty_span: span, user_ty: PatternTypeProjection::from_user_type(user_ty),
variance: ty::Variance::Covariant, user_ty_span: span,
variance: ty::Variance::Covariant,
},
}; };
} }
@ -808,11 +829,13 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
kind: Box::new( kind: Box::new(
PatternKind::AscribeUserType { PatternKind::AscribeUserType {
subpattern: pattern, subpattern: pattern,
/// Note that use `Contravariant` here. See the ascription: Ascription {
/// `variance` field documentation for details. /// Note that use `Contravariant` here. See the
variance: ty::Variance::Contravariant, /// `variance` field documentation for details.
user_ty, variance: ty::Variance::Contravariant,
user_ty_span: span, user_ty,
user_ty_span: span,
},
} }
), ),
ty: value.ty, ty: value.ty,
@ -1105,14 +1128,18 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
PatternKind::Wild => PatternKind::Wild, PatternKind::Wild => PatternKind::Wild,
PatternKind::AscribeUserType { PatternKind::AscribeUserType {
ref subpattern, ref subpattern,
variance, ascription: Ascription {
ref user_ty, variance,
user_ty_span, ref user_ty,
user_ty_span,
},
} => PatternKind::AscribeUserType { } => PatternKind::AscribeUserType {
subpattern: subpattern.fold_with(folder), subpattern: subpattern.fold_with(folder),
user_ty: user_ty.fold_with(folder), ascription: Ascription {
variance, user_ty: user_ty.fold_with(folder),
user_ty_span, variance,
user_ty_span,
},
}, },
PatternKind::Binding { PatternKind::Binding {
mutability, 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