diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 409b6c038ab..bc7bb485563 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -284,6 +284,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { .. }, user_ty: pat_ascription_ty, + variance: _, user_ty_span, } => { let place = @@ -310,6 +311,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { source_info: ty_source_info, kind: StatementKind::AscribeUserType( place, + // We always use invariant as the variance here. This is because the + // variance field from the ascription refers to the variance to use + // when applying the type to the value being matched, but this + // ascription applies rather to the type of the binding. e.g., in this + // example: + // + // ``` + // let x: T = + // ``` + // + // We are creating an ascription that defines the type of `x` to be + // exactly `T` (i.e., with invariance). The variance field, in + // contrast, is intended to be used to relate `T` to the type of + // ``. ty::Variance::Invariant, user_ty, ), @@ -541,12 +556,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { PatternKind::Deref { ref subpattern } => { self.visit_bindings(subpattern, pattern_user_ty.deref(), f); } - PatternKind::AscribeUserType { ref subpattern, ref user_ty, user_ty_span } => { + PatternKind::AscribeUserType { + ref subpattern, + ref user_ty, + user_ty_span, + variance: _, + } => { // This corresponds to something like // // ``` // let A::<'a>(_): A<'static> = ...; // ``` + // + // Note that the variance doesn't apply here, as we are tracking the effect + // of `user_ty` on any bindings contained with subpattern. let annotation = (user_ty_span, user_ty.base); let projection = UserTypeProjection { base: self.canonical_user_type_annotations.push(annotation), @@ -628,6 +651,7 @@ struct Ascription<'tcx> { span: Span, source: Place<'tcx>, user_ty: PatternTypeProjection<'tcx>, + variance: ty::Variance, } #[derive(Clone, Debug)] @@ -1321,7 +1345,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { source_info, kind: StatementKind::AscribeUserType( ascription.source.clone(), - ty::Variance::Covariant, + ascription.variance, user_ty, ), }, diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index 7692ad4e683..22bc3506803 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -56,11 +56,19 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { -> Result<(), MatchPair<'pat, 'tcx>> { let tcx = self.hir.tcx(); match *match_pair.pattern.kind { - PatternKind::AscribeUserType { ref subpattern, ref user_ty, user_ty_span } => { + PatternKind::AscribeUserType { + ref subpattern, + 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. candidate.ascriptions.push(Ascription { span: user_ty_span, user_ty: user_ty.clone(), source: match_pair.place.clone(), + variance, }); candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern)); diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index e73cc40c8c6..ea8b2826732 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -3,6 +3,7 @@ use hair::cx::Cx; use hair::cx::to_ref::ToRef; use rustc::middle::region; use rustc::hir; +use rustc::ty; use rustc_data_structures::indexed_vec::Idx; @@ -86,7 +87,8 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, kind: Box::new(PatternKind::AscribeUserType { user_ty: PatternTypeProjection::from_user_type(user_ty), user_ty_span: ty.span, - subpattern: pattern + subpattern: pattern, + variance: ty::Variance::Covariant, }) }; } diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index f52aeded19a..761bca21fec 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -91,6 +91,25 @@ pub enum PatternKind<'tcx> { AscribeUserType { user_ty: PatternTypeProjection<'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, }, @@ -714,6 +733,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { }, user_ty: PatternTypeProjection::from_user_type(user_ty), user_ty_span: span, + variance: ty::Variance::Covariant, }; } @@ -763,6 +783,9 @@ 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, } @@ -1057,11 +1080,13 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> { PatternKind::Wild => PatternKind::Wild, PatternKind::AscribeUserType { ref subpattern, + 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, }, PatternKind::Binding { diff --git a/src/test/ui/nll/issue-57280-1.rs b/src/test/ui/nll/issue-57280-1.rs new file mode 100644 index 00000000000..356c477f1ba --- /dev/null +++ b/src/test/ui/nll/issue-57280-1.rs @@ -0,0 +1,21 @@ +#![feature(nll)] + +// compile-pass + +trait Foo<'a> { + const C: &'a u32; +} + +impl<'a, T> Foo<'a> for T { + const C: &'a u32 = &22; +} + +fn foo() { + let a = 22; + match &a { + <() as Foo<'static>>::C => { } + &_ => { } + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-57280.rs b/src/test/ui/nll/issue-57280.rs new file mode 100644 index 00000000000..4fe6a96f5dc --- /dev/null +++ b/src/test/ui/nll/issue-57280.rs @@ -0,0 +1,22 @@ +#![feature(nll)] + +// compile-pass + +trait Foo { + const BLAH: &'static str; +} + +struct Placeholder; + +impl Foo for Placeholder { + const BLAH: &'static str = "hi"; +} + +fn foo(x: &str) { + match x { + ::BLAH => { } + _ => { } + } +} + +fn main() {}