]> git.lizzy.rs Git - rust.git/commitdiff
Fix bug in associated constant type annotations.
authorDavid Wood <david@davidtw.co>
Thu, 3 Jan 2019 14:38:59 +0000 (15:38 +0100)
committerDavid Wood <david@davidtw.co>
Thu, 3 Jan 2019 18:33:05 +0000 (19:33 +0100)
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.

src/librustc_mir/build/matches/mod.rs
src/librustc_mir/build/matches/simplify.rs
src/librustc_mir/hair/cx/block.rs
src/librustc_mir/hair/pattern/mod.rs
src/test/ui/nll/issue-57280-1.rs [new file with mode: 0644]
src/test/ui/nll/issue-57280.rs [new file with mode: 0644]

index fe5bc6e19db659bb4af1308d74ca7199298a7bc5..36a8b72c3ea57264a7bf17caef8541b0cc439ce8 100644 (file)
@@ -284,6 +284,7 @@ pub(super) fn expr_into_pattern(
                     ..
                 },
                 user_ty: pat_ascription_ty,
+                variance: _,
                 user_ty_span,
             } => {
                 let place =
@@ -310,6 +311,20 @@ pub(super) fn expr_into_pattern(
                         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 = <expr>
+                            // ```
+                            //
+                            // 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
+                            // `<expr>`.
                             ty::Variance::Invariant,
                             user_ty,
                         ),
@@ -541,12 +556,20 @@ pub(super) fn visit_bindings(
             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 @@ fn ascribe_types<'pat>(
                     source_info,
                     kind: StatementKind::AscribeUserType(
                         ascription.source.clone(),
-                        ty::Variance::Covariant,
+                        ascription.variance,
                         user_ty,
                     ),
                 },
index 7692ad4e6834c7fd7a60889cfc5ea16360da51e7..22bc3506803f23840f81b42d3ae29517b4826c22 100644 (file)
@@ -56,11 +56,19 @@ fn simplify_match_pair<'pat>(&mut self,
                                  -> 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));
index e73cc40c8c6e4ca19a84b14ed754281f6782d633..ea8b2826732b455c54e8dcd6023645641a080888 100644 (file)
@@ -3,6 +3,7 @@
 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,
                                     })
                                 };
                             }
index 10d2d7bc1b18b7cb63bc724e5447c51d76a937b5..ac6f9ab588b35ae2b4187dd2275388505ae25adb 100644 (file)
@@ -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 @@ fn lower_variant_or_leaf(
                 },
                 user_ty: PatternTypeProjection::from_user_type(user_ty),
                 user_ty_span: span,
+                variance: ty::Variance::Covariant,
             };
         }
 
@@ -763,6 +783,9 @@ fn lower_path(&mut self,
                                         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 @@ fn super_fold_with<F: PatternFolder<'tcx>>(&self, folder: &mut F) -> Self {
             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 (file)
index 0000000..356c477
--- /dev/null
@@ -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 (file)
index 0000000..4fe6a96
--- /dev/null
@@ -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 {
+        <Placeholder as Foo>::BLAH => { }
+        _ => { }
+    }
+}
+
+fn main() {}