about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-07-03 07:24:24 +0000
committerbors <bors@rust-lang.org>2021-07-03 07:24:24 +0000
commit701496384fd2d46557b3f013b50860c69c9848cc (patch)
treeb93a1be018014080bb5086d5d8818624cd918e3e
parentfdd9a071474d898556891d5146fd9e8d1a10a077 (diff)
parent5e178b29b43adbd3313fbc70037cf7aba0c98a56 (diff)
downloadrust-701496384fd2d46557b3f013b50860c69c9848cc.tar.gz
rust-701496384fd2d46557b3f013b50860c69c9848cc.zip
Auto merge of #86571 - fee1-dead:const-trait-impl-fix, r=jackh726
deny using default function in impl const Trait

Fixes #79450.

I don't know if my implementation is correct:

 - The check is in `rustc_passes::check_const`, should I put it somewhere else instead?
 - Is my approach (to checking the impl) optimal? It works for the current tests, but it might have some issues or there might be a better way of doing this.
-rw-r--r--compiler/rustc_passes/src/check_const.rs62
-rw-r--r--compiler/rustc_passes/src/lib.rs1
-rw-r--r--src/test/ui/consts/const-float-classify.rs3
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs3
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs3
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs3
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs3
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.rs20
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.stderr10
9 files changed, 108 insertions, 0 deletions
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index da713566c31..d783852aaca 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -13,6 +13,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_middle::hir::map::Map;
+use rustc_middle::ty;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::parse::feature_err;
@@ -59,12 +60,73 @@ impl NonConstExpr {
 fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
     let mut vis = CheckConstVisitor::new(tcx);
     tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor());
+    tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckConstTraitVisitor::new(tcx));
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers { check_mod_const_bodies, ..*providers };
 }
 
+struct CheckConstTraitVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> CheckConstTraitVisitor<'tcx> {
+    fn new(tcx: TyCtxt<'tcx>) -> Self {
+        CheckConstTraitVisitor { tcx }
+    }
+}
+
+impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor<'tcx> {
+    /// check for const trait impls, and errors if the impl uses provided/default functions
+    /// of the trait being implemented; as those provided functions can be non-const.
+    fn visit_item(&mut self, item: &'hir hir::Item<'hir>) {
+        let _: Option<_> = try {
+            if let hir::ItemKind::Impl(ref imp) = item.kind {
+                if let hir::Constness::Const = imp.constness {
+                    let did = imp.of_trait.as_ref()?.trait_def_id()?;
+                    let trait_fn_cnt = self
+                        .tcx
+                        .associated_item_def_ids(did)
+                        .iter()
+                        .filter(|did| {
+                            matches!(
+                                self.tcx.associated_item(**did),
+                                ty::AssocItem { kind: ty::AssocKind::Fn, .. }
+                            )
+                        })
+                        .count();
+
+                    let impl_fn_cnt = imp
+                        .items
+                        .iter()
+                        .filter(|it| matches!(it.kind, hir::AssocItemKind::Fn { .. }))
+                        .count();
+
+                    // number of trait functions unequal to functions in impl,
+                    // meaning that one or more provided/default functions of the
+                    // trait are used.
+                    if trait_fn_cnt != impl_fn_cnt {
+                        self.tcx
+                            .sess
+                            .struct_span_err(
+                                item.span,
+                                "const trait implementations may not use default functions",
+                            )
+                            .emit();
+                    }
+                }
+            }
+        };
+    }
+
+    fn visit_trait_item(&mut self, _: &'hir hir::TraitItem<'hir>) {}
+
+    fn visit_impl_item(&mut self, _: &'hir hir::ImplItem<'hir>) {}
+
+    fn visit_foreign_item(&mut self, _: &'hir hir::ForeignItem<'hir>) {}
+}
+
 #[derive(Copy, Clone)]
 struct CheckConstVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index 28633faa205..cadb8d23580 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -10,6 +10,7 @@
 #![feature(iter_zip)]
 #![feature(nll)]
 #![feature(min_specialization)]
+#![feature(try_blocks)]
 #![recursion_limit = "256"]
 
 #[macro_use]
diff --git a/src/test/ui/consts/const-float-classify.rs b/src/test/ui/consts/const-float-classify.rs
index 36fec9976be..95e7f9e9c83 100644
--- a/src/test/ui/consts/const-float-classify.rs
+++ b/src/test/ui/consts/const-float-classify.rs
@@ -53,6 +53,9 @@ impl const PartialEq<NonDet> for bool {
     fn eq(&self, _: &NonDet) -> bool {
         true
     }
+    fn ne(&self, _: &NonDet) -> bool {
+        false
+    }
 }
 
 // The result of the `is_sign` methods are not checked for correctness, since LLVM does not
diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs
index ec6f45f956d..44814b0654e 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs
@@ -17,6 +17,9 @@ impl const PartialEq for Int {
     fn eq(&self, rhs: &Self) -> bool {
         self.0 == rhs.0
     }
+    fn ne(&self, other: &Self) -> bool {
+        !self.eq(other)
+    }
 }
 
 pub trait Plus {
diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs b/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs
index c37990b1af3..47eed89d03d 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs
@@ -12,6 +12,9 @@ impl const PartialEq for S {
     fn eq(&self, _: &S) -> bool {
         true
     }
+    fn ne(&self, other: &S) -> bool {
+        !self.eq(other)
+    }
 }
 
 const fn equals_self<T: PartialEq>(t: &T) -> bool {
diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs b/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs
index d553b2ab8ec..00a3c7f51fe 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs
@@ -11,6 +11,9 @@ impl const PartialEq for S {
     fn eq(&self, _: &S) -> bool {
         true
     }
+    fn ne(&self, other: &S) -> bool {
+        !self.eq(other)
+    }
 }
 
 // This duplicate bound should not result in ambiguities. It should be equivalent to a single const
diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs b/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs
index 74b0d5fbe47..953a6511199 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs
@@ -12,6 +12,9 @@ impl const PartialEq for S {
     fn eq(&self, _: &S) -> bool {
         true
     }
+    fn ne(&self, other: &S) -> bool {
+        !self.eq(other)
+    }
 }
 
 const fn equals_self<T: PartialEq>(t: &T) -> bool {
diff --git a/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.rs b/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.rs
new file mode 100644
index 00000000000..4ff4fa0d83b
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.rs
@@ -0,0 +1,20 @@
+#![feature(const_trait_impl)]
+#![allow(incomplete_features)]
+
+trait Tr {
+    fn req(&self);
+
+    fn prov(&self) {
+        println!("lul");
+        self.req();
+    }
+}
+
+struct S;
+
+impl const Tr for S {
+    fn req(&self) {}
+}
+//~^^^ ERROR const trait implementations may not use default functions
+
+fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.stderr b/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.stderr
new file mode 100644
index 00000000000..51a7b18fa8d
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.stderr
@@ -0,0 +1,10 @@
+error: const trait implementations may not use default functions
+  --> $DIR/impl-with-default-fn.rs:15:1
+   |
+LL | / impl const Tr for S {
+LL | |     fn req(&self) {}
+LL | | }
+   | |_^
+
+error: aborting due to previous error
+