Skip to content

Commit e69fcea

Browse files
const fn: allow use of trait impls from bounds
1 parent 71d350e commit e69fcea

File tree

6 files changed

+129
-4
lines changed

6 files changed

+129
-4
lines changed

compiler/rustc_mir/src/transform/check_consts/validation.rs

+36-4
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@ use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported};
44
use rustc_hir::def_id::DefId;
55
use rustc_hir::{self as hir, HirId, LangItem};
66
use rustc_infer::infer::TyCtxtInferExt;
7+
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
78
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
89
use rustc_middle::mir::*;
910
use rustc_middle::ty::cast::CastTy;
1011
use rustc_middle::ty::subst::GenericArgKind;
1112
use rustc_middle::ty::{
1213
self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt, TypeAndMut,
1314
};
15+
use rustc_middle::ty::{Binder, TraitPredicate, TraitRef};
1416
use rustc_span::{sym, Span, Symbol};
1517
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
16-
use rustc_trait_selection::traits::{self, TraitEngine};
18+
use rustc_trait_selection::traits::{self, SelectionContext, TraitEngine};
1719

1820
use std::mem;
1921
use std::ops::Deref;
@@ -765,9 +767,39 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
765767
}
766768
};
767769

768-
// Resolve a trait method call to its concrete implementation, which may be in a
769-
// `const` trait impl.
770-
if self.tcx.features().const_trait_impl {
770+
// Attempting to call a trait method?
771+
if let Some(trait_id) = tcx.trait_of_item(callee) {
772+
if !self.tcx.features().const_trait_impl {
773+
self.check_op(ops::FnCallNonConst(callee));
774+
return;
775+
}
776+
777+
let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
778+
let obligation = Obligation::new(
779+
ObligationCause::dummy(),
780+
param_env,
781+
Binder::bind(TraitPredicate {
782+
trait_ref: TraitRef::from_method(tcx, trait_id, substs),
783+
}),
784+
);
785+
786+
let implsrc = tcx.infer_ctxt().enter(|infcx| {
787+
let mut selcx = SelectionContext::new(&infcx);
788+
selcx.select(&obligation).unwrap()
789+
});
790+
791+
// If the method is provided via a where-clause that does not use the `?const`
792+
// opt-out, the call is allowed.
793+
if let Some(ImplSource::Param(_, hir::Constness::Const)) = implsrc {
794+
debug!(
795+
"const_trait_impl: provided {:?} via where-clause in {:?}",
796+
trait_ref, param_env
797+
);
798+
return;
799+
}
800+
801+
// Resolve a trait method call to its concrete implementation, which may be in a
802+
// `const` trait impl.
771803
let instance = Instance::resolve(tcx, param_env, callee, substs);
772804
debug!("Resolving ({:?}) -> {:?}", callee, instance);
773805
if let Ok(Some(func)) = instance {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(const_fn)]
2+
#![feature(const_trait_impl)]
3+
#![feature(const_trait_bound_opt_out)]
4+
#![allow(incomplete_features)]
5+
6+
pub const fn equals_self<T: ?const PartialEq>(t: &T) -> bool {
7+
*t == *t
8+
//~^ ERROR calls in constant functions are limited to constant functions
9+
}
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
2+
--> $DIR/call-generic-method-fail.rs:7:5
3+
|
4+
LL | *t == *t
5+
| ^^^^^^^^
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0015`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// check-pass
2+
3+
#![feature(const_fn)]
4+
#![feature(const_trait_impl)]
5+
#![feature(const_trait_bound_opt_out)]
6+
#![allow(incomplete_features)]
7+
8+
struct S;
9+
10+
impl PartialEq for S {
11+
fn eq(&self, _: &S) -> bool {
12+
true
13+
}
14+
}
15+
16+
const fn equals_self<T: ?const PartialEq>(t: &T) -> bool {
17+
true
18+
}
19+
20+
pub const EQ: bool = equals_self(&S);
21+
22+
// Calling `equals_self` with a type that only has a non-const impl is fine, because we opted out.
23+
24+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// FIXME(jschievink): this is not rejected correctly (only when the non-const impl is actually used)
2+
// ignore-test
3+
4+
#![feature(const_fn)]
5+
#![feature(const_trait_impl)]
6+
#![allow(incomplete_features)]
7+
8+
struct S;
9+
10+
impl PartialEq for S {
11+
fn eq(&self, _: &S) -> bool {
12+
true
13+
}
14+
}
15+
16+
const fn equals_self<T: PartialEq>(t: &T) -> bool {
17+
true
18+
}
19+
20+
// Calling `equals_self` with something that has a non-const impl should throw an error, despite
21+
// it not using the impl.
22+
23+
pub const EQ: bool = equals_self(&S);
24+
//~^ ERROR
25+
26+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//! Basic test for calling methods on generic type parameters in `const fn`.
2+
3+
// check-pass
4+
5+
#![feature(const_fn)]
6+
#![feature(const_trait_impl)]
7+
#![allow(incomplete_features)]
8+
9+
struct S;
10+
11+
impl const PartialEq for S {
12+
fn eq(&self, _: &S) -> bool {
13+
true
14+
}
15+
}
16+
17+
const fn equals_self<T: PartialEq>(t: &T) -> bool {
18+
*t == *t
19+
}
20+
21+
pub const EQ: bool = equals_self(&S);
22+
23+
fn main() {}

0 commit comments

Comments
 (0)