Skip to content

Commit 4933793

Browse files
committed
Fix bug in associated constant type annotations.
This commit reverses the variance used when relating types from the type annotation of an associated constant - this matches the behaviour of the lexical borrow checker and fixes a bug whereby matching a `&'a str` against a `&'static str` would produce an error.
1 parent ec19464 commit 4933793

File tree

6 files changed

+106
-4
lines changed

6 files changed

+106
-4
lines changed

src/librustc_mir/build/matches/mod.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
284284
..
285285
},
286286
user_ty: pat_ascription_ty,
287+
variance: _,
287288
user_ty_span,
288289
} => {
289290
let place =
@@ -310,6 +311,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
310311
source_info: ty_source_info,
311312
kind: StatementKind::AscribeUserType(
312313
place,
314+
// We always use invariant as the variance here. This is because the
315+
// variance field from the ascription refers to the variance to use
316+
// when applying the type to the value being matched, but this
317+
// ascription applies rather to the type of the binding. e.g., in this
318+
// example:
319+
//
320+
// ```
321+
// let x: T = <expr>
322+
// ```
323+
//
324+
// We are creating an ascription that defines the type of `x` to be
325+
// exactly `T` (i.e., with invariance). The variance field, in
326+
// contrast, is intended to be used to relate `T` to the type of
327+
// `<expr>`.
313328
ty::Variance::Invariant,
314329
user_ty,
315330
),
@@ -541,12 +556,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
541556
PatternKind::Deref { ref subpattern } => {
542557
self.visit_bindings(subpattern, pattern_user_ty.deref(), f);
543558
}
544-
PatternKind::AscribeUserType { ref subpattern, ref user_ty, user_ty_span } => {
559+
PatternKind::AscribeUserType {
560+
ref subpattern,
561+
ref user_ty,
562+
user_ty_span,
563+
variance: _,
564+
} => {
545565
// This corresponds to something like
546566
//
547567
// ```
548568
// let A::<'a>(_): A<'static> = ...;
549569
// ```
570+
//
571+
// Note that the variance doesn't apply here, as we are tracking the effect
572+
// of `user_ty` on any bindings contained with subpattern.
550573
let annotation = (user_ty_span, user_ty.base);
551574
let projection = UserTypeProjection {
552575
base: self.canonical_user_type_annotations.push(annotation),
@@ -628,6 +651,7 @@ struct Ascription<'tcx> {
628651
span: Span,
629652
source: Place<'tcx>,
630653
user_ty: PatternTypeProjection<'tcx>,
654+
variance: ty::Variance,
631655
}
632656

633657
#[derive(Clone, Debug)]
@@ -1321,7 +1345,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
13211345
source_info,
13221346
kind: StatementKind::AscribeUserType(
13231347
ascription.source.clone(),
1324-
ty::Variance::Covariant,
1348+
ascription.variance,
13251349
user_ty,
13261350
),
13271351
},

src/librustc_mir/build/matches/simplify.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,19 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
5656
-> Result<(), MatchPair<'pat, 'tcx>> {
5757
let tcx = self.hir.tcx();
5858
match *match_pair.pattern.kind {
59-
PatternKind::AscribeUserType { ref subpattern, ref user_ty, user_ty_span } => {
59+
PatternKind::AscribeUserType {
60+
ref subpattern,
61+
variance,
62+
ref user_ty,
63+
user_ty_span
64+
} => {
65+
// Apply the type ascription to the value at `match_pair.place`, which is the
66+
// value being matched, taking the variance field into account.
6067
candidate.ascriptions.push(Ascription {
6168
span: user_ty_span,
6269
user_ty: user_ty.clone(),
6370
source: match_pair.place.clone(),
71+
variance,
6472
});
6573

6674
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));

src/librustc_mir/hair/cx/block.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use hair::cx::Cx;
33
use hair::cx::to_ref::ToRef;
44
use rustc::middle::region;
55
use rustc::hir;
6+
use rustc::ty;
67

78
use rustc_data_structures::indexed_vec::Idx;
89

@@ -86,7 +87,8 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
8687
kind: Box::new(PatternKind::AscribeUserType {
8788
user_ty: PatternTypeProjection::from_user_type(user_ty),
8889
user_ty_span: ty.span,
89-
subpattern: pattern
90+
subpattern: pattern,
91+
variance: ty::Variance::Covariant,
9092
})
9193
};
9294
}

src/librustc_mir/hair/pattern/mod.rs

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

@@ -714,6 +733,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
714733
},
715734
user_ty: PatternTypeProjection::from_user_type(user_ty),
716735
user_ty_span: span,
736+
variance: ty::Variance::Covariant,
717737
};
718738
}
719739

@@ -763,6 +783,9 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
763783
kind: Box::new(
764784
PatternKind::AscribeUserType {
765785
subpattern: pattern,
786+
/// Note that use `Contravariant` here. See the
787+
/// `variance` field documentation for details.
788+
variance: ty::Variance::Contravariant,
766789
user_ty,
767790
user_ty_span: span,
768791
}
@@ -1057,11 +1080,13 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
10571080
PatternKind::Wild => PatternKind::Wild,
10581081
PatternKind::AscribeUserType {
10591082
ref subpattern,
1083+
variance,
10601084
ref user_ty,
10611085
user_ty_span,
10621086
} => PatternKind::AscribeUserType {
10631087
subpattern: subpattern.fold_with(folder),
10641088
user_ty: user_ty.fold_with(folder),
1089+
variance,
10651090
user_ty_span,
10661091
},
10671092
PatternKind::Binding {

src/test/ui/nll/issue-57280-1.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![feature(nll)]
2+
3+
// compile-pass
4+
5+
trait Foo<'a> {
6+
const C: &'a u32;
7+
}
8+
9+
impl<'a, T> Foo<'a> for T {
10+
const C: &'a u32 = &22;
11+
}
12+
13+
fn foo() {
14+
let a = 22;
15+
match &a {
16+
<() as Foo<'static>>::C => { }
17+
&_ => { }
18+
}
19+
}
20+
21+
fn main() {}

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

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![feature(nll)]
2+
3+
// compile-pass
4+
5+
trait Foo {
6+
const BLAH: &'static str;
7+
}
8+
9+
struct Placeholder;
10+
11+
impl Foo for Placeholder {
12+
const BLAH: &'static str = "hi";
13+
}
14+
15+
fn foo(x: &str) {
16+
match x {
17+
<Placeholder as Foo>::BLAH => { }
18+
_ => { }
19+
}
20+
}
21+
22+
fn main() {}

0 commit comments

Comments
 (0)