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.
This commit is contained in:
bors 2021-07-03 07:24:24 +00:00
commit 701496384f
9 changed files with 108 additions and 0 deletions

View File

@ -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>,

View File

@ -10,6 +10,7 @@
#![feature(iter_zip)]
#![feature(nll)]
#![feature(min_specialization)]
#![feature(try_blocks)]
#![recursion_limit = "256"]
#[macro_use]

View File

@ -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

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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() {}

View File

@ -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