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:
parent
2e08bb1dd2
commit
ee82d09b6c
@ -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
|
||||||
//
|
//
|
||||||
|
@ -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.
|
||||||
|
@ -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,
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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,
|
||||||
|
30
src/test/ui/nll/issue-58299.rs
Normal file
30
src/test/ui/nll/issue-58299.rs
Normal 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() {}
|
20
src/test/ui/nll/issue-58299.stderr
Normal file
20
src/test/ui/nll/issue-58299.stderr
Normal 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
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user