diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index da713566c3121..d783852aacadd 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 28633faa205d0..cadb8d2358060 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 36fec9976be37..95e7f9e9c837a 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 ec6f45f956d75..44814b0654e91 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 c37990b1af3dd..47eed89d03d2d 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 d553b2ab8ec47..00a3c7f51fe90 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 74b0d5fbe474b..953a65111990e 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 0000000000000..4ff4fa0d83b2c --- /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 0000000000000..51a7b18fa8d55 --- /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 +