]> git.lizzy.rs Git - rust.git/commitdiff
Require `~const` qualifier on trait bounds in specializing impls if present in base...
authorBen Reeves <benwolverine2019@gmail.com>
Sun, 6 Nov 2022 06:07:06 +0000 (01:07 -0500)
committerBen Reeves <benwolverine2019@gmail.com>
Thu, 10 Nov 2022 18:37:07 +0000 (12:37 -0600)
compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs [new file with mode: 0644]
src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr [new file with mode: 0644]
src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs [new file with mode: 0644]
src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr [new file with mode: 0644]
src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs [deleted file]
src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr [deleted file]
src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs
src/test/ui/rfc-2632-const-trait-impl/specializing-constness.rs
src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr

index 9c7150b79240a83a17393d900ccdd3a20a32fbdf..f3cb558ef70938c8e81420651c7d5891f9a9086b 100644 (file)
@@ -391,7 +391,7 @@ fn check_predicates<'tcx>(
     );
 
     for (predicate, span) in impl1_predicates {
-        if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(predicate, *pred2)) {
+        if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) {
             check_specialization_on(tcx, predicate, span)
         }
     }
@@ -400,8 +400,8 @@ fn check_predicates<'tcx>(
 /// Checks if some predicate on the specializing impl (`predicate1`) is the same
 /// as some predicate on the base impl (`predicate2`).
 ///
-/// This is slightly more complicated than simple syntactic equivalence, since
-/// we want to equate `T: Tr` with `T: ~const Tr` so this can work:
+/// This basically just checks syntactic equivalence, but is a little more
+/// forgiving since we want to equate `T: Tr` with `T: ~const Tr` so this can work:
 ///
 /// ```ignore (illustrative)
 /// #[rustc_specialization_trait]
@@ -410,27 +410,54 @@ fn check_predicates<'tcx>(
 /// impl<T: Bound> Tr for T { }
 /// impl<T: ~const Bound + Specialize> const Tr for T { }
 /// ```
+///
+/// However, we *don't* want to allow the reverse, i.e., when the bound on the
+/// specializing impl is not as const as the bound on the base impl:
+///
+/// ```ignore (illustrative)
+/// impl<T: ~const Bound> const Tr for T { }
+/// impl<T: Bound + Specialize> const Tr for T { } // should be T: ~const Bound
+/// ```
+///
+/// So we make that check in this function and try to raise a helpful error message.
 fn trait_predicates_eq<'tcx>(
+    tcx: TyCtxt<'tcx>,
     predicate1: ty::Predicate<'tcx>,
     predicate2: ty::Predicate<'tcx>,
+    span: Span,
 ) -> bool {
-    let predicate_kind_without_constness = |kind: ty::PredicateKind<'tcx>| match kind {
-        ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity }) => {
-            ty::PredicateKind::Trait(ty::TraitPredicate {
-                trait_ref,
-                constness: ty::BoundConstness::NotConst,
-                polarity,
-            })
+    let pred1_kind = predicate1.kind().no_bound_vars();
+    let pred2_kind = predicate2.kind().no_bound_vars();
+    let (trait_pred1, trait_pred2) = match (pred1_kind, pred2_kind) {
+        (Some(ty::PredicateKind::Trait(pred1)), Some(ty::PredicateKind::Trait(pred2))) => {
+            (pred1, pred2)
         }
-        _ => kind,
+        // Just use plain syntactic equivalence if either of the predicates aren't
+        // trait predicates or have bound vars.
+        _ => return pred1_kind == pred2_kind,
+    };
+
+    let predicates_equal_modulo_constness = {
+        let pred1_unconsted =
+            ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred1 };
+        let pred2_unconsted =
+            ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred2 };
+        pred1_unconsted == pred2_unconsted
     };
 
-    // We rely on `check_constness` above to ensure that pred1 is const if pred2
-    // is const.
-    let pred1_kind_not_const = predicate1.kind().map_bound(predicate_kind_without_constness);
-    let pred2_kind_not_const = predicate2.kind().map_bound(predicate_kind_without_constness);
+    if !predicates_equal_modulo_constness {
+        return false;
+    }
+
+    // Check that the predicate on the specializing impl is at least as const as
+    // the one on the base.
+    if trait_pred2.constness == ty::BoundConstness::ConstIfConst
+        && trait_pred1.constness == ty::BoundConstness::NotConst
+    {
+        tcx.sess.struct_span_err(span, "missing `~const` qualifier").emit();
+    }
 
-    pred1_kind_not_const == pred2_kind_not_const
+    true
 }
 
 #[instrument(level = "debug", skip(tcx))]
diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs
new file mode 100644 (file)
index 0000000..3ac9099
--- /dev/null
@@ -0,0 +1,46 @@
+// Tests that trait bounds on specializing trait impls must be `~const` if the
+// same bound is present on the default impl and is `~const` there.
+
+#![feature(const_trait_impl)]
+#![feature(rustc_attrs)]
+#![feature(min_specialization)]
+
+#[rustc_specialization_trait]
+trait Specialize {}
+
+#[const_trait]
+trait Foo {}
+
+#[const_trait]
+trait Bar {}
+
+// bgr360: I was only able to exercise the code path that raises the
+// "missing ~const qualifier" error by making this base impl non-const, even
+// though that doesn't really make sense to do. As seen below, if the base impl
+// is made const, rustc fails earlier with an overlapping impl failure.
+impl<T> Bar for T
+where
+    T: ~const Foo,
+{}
+
+impl<T> Bar for T
+where
+    T: Foo, //~ ERROR missing `~const` qualifier
+    T: Specialize,
+{}
+
+#[const_trait]
+trait Baz {}
+
+impl<T> const Baz for T
+where
+    T: ~const Foo,
+{}
+
+impl<T> const Baz for T //~ ERROR conflicting implementations of trait `Baz`
+where
+    T: Foo,
+    T: Specialize,
+{}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr
new file mode 100644 (file)
index 0000000..583c4ce
--- /dev/null
@@ -0,0 +1,18 @@
+error: missing `~const` qualifier
+  --> $DIR/const-default-bound-non-const-specialized-bound.rs:28:8
+   |
+LL |     T: Foo,
+   |        ^^^
+
+error[E0119]: conflicting implementations of trait `Baz`
+  --> $DIR/const-default-bound-non-const-specialized-bound.rs:40:1
+   |
+LL | impl<T> const Baz for T
+   | ----------------------- first implementation here
+...
+LL | impl<T> const Baz for T
+   | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs
new file mode 100644 (file)
index 0000000..a3bb9b3
--- /dev/null
@@ -0,0 +1,26 @@
+// Tests that specializing trait impls must be at least as const as the default impl.
+
+#![feature(const_trait_impl)]
+#![feature(min_specialization)]
+
+#[const_trait]
+trait Value {
+    fn value() -> u32;
+}
+
+impl<T> const Value for T {
+    default fn value() -> u32 {
+        0
+    }
+}
+
+struct FortyTwo;
+
+impl Value for FortyTwo { //~ ERROR cannot specialize on const impl with non-const impl
+    fn value() -> u32 {
+        println!("You can't do that (constly)");
+        42
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr
new file mode 100644 (file)
index 0000000..2476680
--- /dev/null
@@ -0,0 +1,8 @@
+error: cannot specialize on const impl with non-const impl
+  --> $DIR/const-default-impl-non-const-specialized-impl.rs:19:1
+   |
+LL | impl Value for FortyTwo {
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs
deleted file mode 100644 (file)
index 79dd495..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-// Tests that a const default trait impl cannot be specialized by a non-const
-// trait impl.
-
-#![feature(const_trait_impl)]
-#![feature(min_specialization)]
-
-#[const_trait]
-trait Value {
-    fn value() -> u32;
-}
-
-impl<T> const Value for T {
-    default fn value() -> u32 {
-        0
-    }
-}
-
-struct FortyTwo;
-
-impl Value for FortyTwo { //~ ERROR cannot specialize on const impl with non-const impl
-    fn value() -> u32 {
-        println!("You can't do that (constly)");
-        42
-    }
-}
-
-fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr
deleted file mode 100644 (file)
index 5232a86..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-error: cannot specialize on const impl with non-const impl
-  --> $DIR/const-default-non-const-specialized.rs:20:1
-   |
-LL | impl Value for FortyTwo {
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to previous error
-
index 3370aebf0634a98c68770a109e093135bce9734f..1e6b1c6513b39a5699e39e39bd5ea6a6c73a3b89 100644 (file)
@@ -1,5 +1,6 @@
-// Tests that `T: Foo` and `T: ~const Foo` are treated as equivalent for the
-// purposes of min_specialization.
+// Tests that `T: ~const Foo` in a specializing impl is treated as equivalent to
+// `T: Foo` in the default impl for the purposes of specialization (i.e., it
+// does not think that the user is attempting to specialize on trait `Foo`).
 
 // check-pass
 
@@ -27,4 +28,18 @@ impl<T> const Bar for T
     T: Specialize,
 {}
 
+#[const_trait]
+trait Baz {}
+
+impl<T> const Baz for T
+where
+    T: Foo,
+{}
+
+impl<T> const Baz for T
+where
+    T: ~const Foo,
+    T: Specialize,
+{}
+
 fn main() {}
index ff0cd489d47440cd448e46f61675335f6f5a4930..9ab170f092006a05799cbca6298c1262b7b5ac93 100644 (file)
@@ -17,7 +17,9 @@ impl<T: ~const Default> const A for T {
     }
 }
 
-impl<T: Default + Sup> A for T { //~ ERROR: cannot specialize
+impl<T: Default + Sup> A for T {
+//~^ ERROR: cannot specialize
+//~| ERROR: missing `~const` qualifier
     fn a() -> u32 {
         3
     }
index 3296c109c4e7361bf7b2b4904577bf374725209b..281ba82d64429e38a4b22d2b24cf3c9b138d679b 100644 (file)
@@ -1,8 +1,14 @@
-error: cannot specialize on trait `Default`
+error: cannot specialize on const impl with non-const impl
+  --> $DIR/specializing-constness.rs:20:1
+   |
+LL | impl<T: Default + Sup> A for T {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing `~const` qualifier
   --> $DIR/specializing-constness.rs:20:9
    |
 LL | impl<T: Default + Sup> A for T {
    |         ^^^^^^^
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors