From 22a8d46ed36271ed3a3d73b7747ab3a300b5af10 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 23 Jun 2021 18:37:26 +0800 Subject: [PATCH 1/2] deny using default function in impl const Trait --- compiler/rustc_passes/src/check_const.rs | 59 +++++++++++++++++++ compiler/rustc_passes/src/lib.rs | 1 + src/test/ui/consts/const-float-classify.rs | 3 + .../call-const-trait-method-pass.rs | 3 + .../call-generic-method-chain.rs | 3 + .../call-generic-method-dup-bound.rs | 3 + .../call-generic-method-pass.rs | 3 + .../impl-with-default-fn.rs | 20 +++++++ .../impl-with-default-fn.stderr | 10 ++++ 9 files changed, 105 insertions(+) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.stderr diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index da713566c31..d62f3d5e6d6 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -13,6 +13,7 @@ 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,70 @@ fn required_feature_gates(self) -> Option<&'static [Symbol]> { 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()); + if tcx.features().enabled(sym::const_trait_impl) { + 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> { + 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(); + + 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 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: &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: &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 + From 5e178b29b43adbd3313fbc70037cf7aba0c98a56 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 28 Jun 2021 11:13:32 +0800 Subject: [PATCH 2/2] Do the check even when the feature is not enabled --- compiler/rustc_passes/src/check_const.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index d62f3d5e6d6..d783852aaca 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -60,9 +60,7 @@ fn required_feature_gates(self) -> Option<&'static [Symbol]> { 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()); - if tcx.features().enabled(sym::const_trait_impl) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckConstTraitVisitor::new(tcx)); - } + tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckConstTraitVisitor::new(tcx)); } pub(crate) fn provide(providers: &mut Providers) { @@ -80,6 +78,8 @@ fn new(tcx: TyCtxt<'tcx>) -> Self { } 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 { @@ -103,6 +103,9 @@ fn visit_item(&mut self, item: &'hir hir::Item<'hir>) { .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