Skip to content

Commit ee82d09

Browse files
committed
Check user type annotations for range patterns.
This commit builds on the fix from rust-lang#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).
1 parent 2e08bb1 commit ee82d09

File tree

7 files changed

+176
-85
lines changed

7 files changed

+176
-85
lines changed

src/librustc_mir/build/matches/mod.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::build::scope::{CachedBlock, DropKind};
77
use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard};
88
use crate::build::{BlockAnd, BlockAndExtension, Builder};
99
use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
10-
use crate::hair::*;
10+
use crate::hair::{self, *};
1111
use rustc::mir::*;
1212
use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty};
1313
use rustc::ty::layout::VariantIdx;
@@ -283,9 +283,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
283283
},
284284
..
285285
},
286-
user_ty: pat_ascription_ty,
287-
variance: _,
288-
user_ty_span,
286+
ascription: hair::pattern::Ascription {
287+
user_ty: pat_ascription_ty,
288+
variance: _,
289+
user_ty_span,
290+
},
289291
} => {
290292
let place =
291293
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard);
@@ -560,9 +562,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
560562
}
561563
PatternKind::AscribeUserType {
562564
ref subpattern,
563-
ref user_ty,
564-
user_ty_span,
565-
variance: _,
565+
ascription: hair::pattern::Ascription {
566+
ref user_ty,
567+
user_ty_span,
568+
variance: _,
569+
},
566570
} => {
567571
// This corresponds to something like
568572
//

src/librustc_mir/build/matches/simplify.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
1515
use crate::build::Builder;
1616
use crate::build::matches::{Ascription, Binding, MatchPair, Candidate};
17-
use crate::hair::*;
17+
use crate::hair::{self, *};
1818
use rustc::ty;
1919
use rustc::ty::layout::{Integer, IntegerExt, Size};
2020
use syntax::attr::{SignedInt, UnsignedInt};
@@ -58,9 +58,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
5858
match *match_pair.pattern.kind {
5959
PatternKind::AscribeUserType {
6060
ref subpattern,
61-
variance,
62-
ref user_ty,
63-
user_ty_span
61+
ascription: hair::pattern::Ascription {
62+
variance,
63+
ref user_ty,
64+
user_ty_span,
65+
},
6466
} => {
6567
// Apply the type ascription to the value at `match_pair.place`, which is the
6668
// value being matched, taking the variance field into account.

src/librustc_mir/hair/cx/block.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::hair::*;
1+
use crate::hair::{self, *};
22
use crate::hair::cx::Cx;
33
use crate::hair::cx::to_ref::ToRef;
44
use rustc::middle::region;
@@ -83,10 +83,12 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
8383
ty: pattern.ty,
8484
span: pattern.span,
8585
kind: Box::new(PatternKind::AscribeUserType {
86-
user_ty: PatternTypeProjection::from_user_type(user_ty),
87-
user_ty_span: ty.span,
86+
ascription: hair::pattern::Ascription {
87+
user_ty: PatternTypeProjection::from_user_type(user_ty),
88+
user_ty_span: ty.span,
89+
variance: ty::Variance::Covariant,
90+
},
8891
subpattern: pattern,
89-
variance: ty::Variance::Covariant,
9092
})
9193
};
9294
}

src/librustc_mir/hair/pattern/_match.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -871,18 +871,24 @@ impl<'tcx> IntRange<'tcx> {
871871
}
872872

873873
fn from_pat(tcx: TyCtxt<'_, 'tcx, 'tcx>,
874-
pat: &Pattern<'tcx>)
874+
mut pat: &Pattern<'tcx>)
875875
-> Option<IntRange<'tcx>> {
876-
Self::from_ctor(tcx, &match pat.kind {
877-
box PatternKind::Constant { value } => ConstantValue(value),
878-
box PatternKind::Range(PatternRange { lo, hi, ty, end }) => ConstantRange(
879-
lo.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
880-
hi.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
881-
ty,
882-
end,
883-
),
884-
_ => return None,
885-
})
876+
let range = loop {
877+
match pat.kind {
878+
box PatternKind::Constant { value } => break ConstantValue(value),
879+
box PatternKind::Range(PatternRange { lo, hi, ty, end }) => break ConstantRange(
880+
lo.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
881+
hi.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(),
882+
ty,
883+
end,
884+
),
885+
box PatternKind::AscribeUserType { ref subpattern, .. } => {
886+
pat = subpattern;
887+
},
888+
_ => return None,
889+
}
890+
};
891+
Self::from_ctor(tcx, &range)
886892
}
887893

888894
// The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.

src/librustc_mir/hair/pattern/mod.rs

+86-59
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ pub struct Pattern<'tcx> {
5858
}
5959

6060

61-
#[derive(Clone, Debug)]
61+
#[derive(Copy, Clone, Debug, PartialEq)]
6262
pub struct PatternTypeProjection<'tcx> {
6363
pub user_ty: CanonicalUserType<'tcx>,
6464
}
@@ -87,33 +87,38 @@ impl<'tcx> PatternTypeProjection<'tcx> {
8787
}
8888
}
8989

90+
#[derive(Copy, Clone, Debug, PartialEq)]
91+
pub struct Ascription<'tcx> {
92+
pub user_ty: PatternTypeProjection<'tcx>,
93+
/// Variance to use when relating the type `user_ty` to the **type of the value being
94+
/// matched**. Typically, this is `Variance::Covariant`, since the value being matched must
95+
/// have a type that is some subtype of the ascribed type.
96+
///
97+
/// Note that this variance does not apply for any bindings within subpatterns. The type
98+
/// assigned to those bindings must be exactly equal to the `user_ty` given here.
99+
///
100+
/// The only place where this field is not `Covariant` is when matching constants, where
101+
/// we currently use `Contravariant` -- this is because the constant type just needs to
102+
/// be "comparable" to the type of the input value. So, for example:
103+
///
104+
/// ```text
105+
/// match x { "foo" => .. }
106+
/// ```
107+
///
108+
/// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should
109+
/// probably be checking for a `PartialEq` impl instead, but this preserves the behavior
110+
/// of the old type-check for now. See #57280 for details.
111+
pub variance: ty::Variance,
112+
pub user_ty_span: Span,
113+
}
114+
90115
#[derive(Clone, Debug)]
91116
pub enum PatternKind<'tcx> {
92117
Wild,
93118

94119
AscribeUserType {
95-
user_ty: PatternTypeProjection<'tcx>,
120+
ascription: Ascription<'tcx>,
96121
subpattern: Pattern<'tcx>,
97-
/// Variance to use when relating the type `user_ty` to the **type of the value being
98-
/// matched**. Typically, this is `Variance::Covariant`, since the value being matched must
99-
/// have a type that is some subtype of the ascribed type.
100-
///
101-
/// Note that this variance does not apply for any bindings within subpatterns. The type
102-
/// assigned to those bindings must be exactly equal to the `user_ty` given here.
103-
///
104-
/// The only place where this field is not `Covariant` is when matching constants, where
105-
/// we currently use `Contravariant` -- this is because the constant type just needs to
106-
/// be "comparable" to the type of the input value. So, for example:
107-
///
108-
/// ```text
109-
/// match x { "foo" => .. }
110-
/// ```
111-
///
112-
/// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should
113-
/// probably be checking for a `PartialEq` impl instead, but this preserves the behavior
114-
/// of the old type-check for now. See #57280 for details.
115-
variance: ty::Variance,
116-
user_ty_span: Span,
117122
},
118123

119124
/// x, ref x, x @ P, etc
@@ -167,18 +172,7 @@ pub enum PatternKind<'tcx> {
167172
},
168173
}
169174

170-
impl<'tcx> PatternKind<'tcx> {
171-
/// If this is a `PatternKind::AscribeUserType` then return the subpattern kind, otherwise
172-
/// return this pattern kind.
173-
fn with_user_type_ascription_subpattern(self) -> Self {
174-
match self {
175-
PatternKind::AscribeUserType { subpattern: Pattern { box kind, .. }, .. } => kind,
176-
kind => kind,
177-
}
178-
}
179-
}
180-
181-
#[derive(Clone, Copy, Debug, PartialEq)]
175+
#[derive(Copy, Clone, Debug, PartialEq)]
182176
pub struct PatternRange<'tcx> {
183177
pub lo: ty::Const<'tcx>,
184178
pub hi: ty::Const<'tcx>,
@@ -405,6 +399,19 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
405399
)
406400
}
407401

402+
fn lower_range_expr(
403+
&mut self,
404+
expr: &'tcx hir::Expr,
405+
) -> (PatternKind<'tcx>, Option<Ascription<'tcx>>) {
406+
match self.lower_lit(expr) {
407+
PatternKind::AscribeUserType {
408+
ascription: lo_ascription,
409+
subpattern: Pattern { kind: box kind, .. },
410+
} => (kind, Some(lo_ascription)),
411+
kind => (kind, None),
412+
}
413+
}
414+
408415
fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> {
409416
let mut ty = self.tables.node_id_to_type(pat.hir_id);
410417

@@ -414,14 +421,10 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
414421
PatKind::Lit(ref value) => self.lower_lit(value),
415422

416423
PatKind::Range(ref lo_expr, ref hi_expr, end) => {
417-
match (
418-
// Look for `PatternKind::Constant` patterns inside of any
419-
// `PatternKind::AscribeUserType` patterns. Type ascriptions can be safely
420-
// ignored for the purposes of lowering a range correctly - these are checked
421-
// elsewhere for well-formedness.
422-
self.lower_lit(lo_expr).with_user_type_ascription_subpattern(),
423-
self.lower_lit(hi_expr).with_user_type_ascription_subpattern(),
424-
) {
424+
let (lo, lo_ascription) = self.lower_range_expr(lo_expr);
425+
let (hi, hi_ascription) = self.lower_range_expr(hi_expr);
426+
427+
let mut kind = match (lo, hi) {
425428
(PatternKind::Constant { value: lo }, PatternKind::Constant { value: hi }) => {
426429
use std::cmp::Ordering;
427430
let cmp = compare_const_vals(
@@ -470,17 +473,33 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
470473
PatternKind::Wild
471474
}
472475
}
473-
}
476+
},
474477
ref pats => {
475478
self.tcx.sess.delay_span_bug(
476479
pat.span,
477-
&format!("found bad range pattern `{:?}` outside of error recovery",
478-
pats),
480+
&format!(
481+
"found bad range pattern `{:?}` outside of error recovery",
482+
pats,
483+
),
479484
);
480485

481486
PatternKind::Wild
487+
},
488+
};
489+
490+
// If we are handling a range with associated constants (e.g.
491+
// `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
492+
// constants somewhere. Have them on the range pattern.
493+
for ascription in &[lo_ascription, hi_ascription] {
494+
if let Some(ascription) = ascription {
495+
kind = PatternKind::AscribeUserType {
496+
ascription: *ascription,
497+
subpattern: Pattern { span: pat.span, ty, kind: Box::new(kind), },
498+
};
482499
}
483500
}
501+
502+
kind
484503
}
485504

486505
PatKind::Path(ref qpath) => {
@@ -756,9 +775,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
756775
ty,
757776
kind: Box::new(kind),
758777
},
759-
user_ty: PatternTypeProjection::from_user_type(user_ty),
760-
user_ty_span: span,
761-
variance: ty::Variance::Covariant,
778+
ascription: Ascription {
779+
user_ty: PatternTypeProjection::from_user_type(user_ty),
780+
user_ty_span: span,
781+
variance: ty::Variance::Covariant,
782+
},
762783
};
763784
}
764785

@@ -808,11 +829,13 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
808829
kind: Box::new(
809830
PatternKind::AscribeUserType {
810831
subpattern: pattern,
811-
/// Note that use `Contravariant` here. See the
812-
/// `variance` field documentation for details.
813-
variance: ty::Variance::Contravariant,
814-
user_ty,
815-
user_ty_span: span,
832+
ascription: Ascription {
833+
/// Note that use `Contravariant` here. See the
834+
/// `variance` field documentation for details.
835+
variance: ty::Variance::Contravariant,
836+
user_ty,
837+
user_ty_span: span,
838+
},
816839
}
817840
),
818841
ty: value.ty,
@@ -1105,14 +1128,18 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
11051128
PatternKind::Wild => PatternKind::Wild,
11061129
PatternKind::AscribeUserType {
11071130
ref subpattern,
1108-
variance,
1109-
ref user_ty,
1110-
user_ty_span,
1131+
ascription: Ascription {
1132+
variance,
1133+
ref user_ty,
1134+
user_ty_span,
1135+
},
11111136
} => PatternKind::AscribeUserType {
11121137
subpattern: subpattern.fold_with(folder),
1113-
user_ty: user_ty.fold_with(folder),
1114-
variance,
1115-
user_ty_span,
1138+
ascription: Ascription {
1139+
user_ty: user_ty.fold_with(folder),
1140+
variance,
1141+
user_ty_span,
1142+
},
11161143
},
11171144
PatternKind::Binding {
11181145
mutability,

src/test/ui/nll/issue-58299.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![allow(dead_code)]
2+
#![feature(nll)]
3+
4+
struct A<'a>(&'a ());
5+
6+
trait Y {
7+
const X: i32;
8+
}
9+
10+
impl Y for A<'static> {
11+
const X: i32 = 10;
12+
}
13+
14+
fn foo<'a>(x: i32) {
15+
match x {
16+
// This uses <A<'a> as Y>::X, but `A<'a>` does not implement `Y`.
17+
A::<'a>::X..=A::<'static>::X => (), //~ ERROR lifetime may not live long enough
18+
_ => (),
19+
}
20+
}
21+
22+
fn bar<'a>(x: i32) {
23+
match x {
24+
// This uses <A<'a> as Y>::X, but `A<'a>` does not implement `Y`.
25+
A::<'static>::X..=A::<'a>::X => (), //~ ERROR lifetime may not live long enough
26+
_ => (),
27+
}
28+
}
29+
30+
fn main() {}

src/test/ui/nll/issue-58299.stderr

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/issue-58299.rs:17:9
3+
|
4+
LL | fn foo<'a>(x: i32) {
5+
| -- lifetime `'a` defined here
6+
...
7+
LL | A::<'a>::X..=A::<'static>::X => (), //~ ERROR lifetime may not live long enough
8+
| ^^^^^^^^^^ requires that `'a` must outlive `'static`
9+
10+
error: lifetime may not live long enough
11+
--> $DIR/issue-58299.rs:25:27
12+
|
13+
LL | fn bar<'a>(x: i32) {
14+
| -- lifetime `'a` defined here
15+
...
16+
LL | A::<'static>::X..=A::<'a>::X => (), //~ ERROR lifetime may not live long enough
17+
| ^^^^^^^^^^ requires that `'a` must outlive `'static`
18+
19+
error: aborting due to 2 previous errors
20+

0 commit comments

Comments
 (0)