do const trait method bounds check later in rustc_const_eval
This commit is contained in:
parent
1bcc26a6b1
commit
f8813cf10e
@ -424,12 +424,31 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
||||
}
|
||||
|
||||
if let ty::FnDef(def_id, substs) = *constant.literal.ty().kind() {
|
||||
// N.B.: When instantiating a trait method as a function item, it does not actually matter
|
||||
// whether the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied
|
||||
// as `const`. If we were to introduce instantiating trait methods as `const fn`s, we would
|
||||
// check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a
|
||||
// `const fn` pointer.
|
||||
//
|
||||
// FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy
|
||||
// `~const FnOnce` or can be coerced to `const fn` pointer.
|
||||
let const_norm = self.tcx().def_kind(def_id) == hir::def::DefKind::AssocFn
|
||||
&& self.tcx().def_kind(ty::DefIdTree::parent(self.tcx(), def_id))
|
||||
== hir::def::DefKind::Trait;
|
||||
|
||||
let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
|
||||
let prev = self.cx.param_env;
|
||||
if const_norm {
|
||||
self.cx.param_env = prev.without_const();
|
||||
}
|
||||
self.cx.normalize_and_prove_instantiated_predicates(
|
||||
def_id,
|
||||
instantiated_predicates,
|
||||
locations,
|
||||
);
|
||||
if const_norm {
|
||||
self.cx.param_env = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,11 @@ use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty,
|
||||
use rustc_middle::ty::{Binder, TraitPredicate, TraitRef, TypeVisitable};
|
||||
use rustc_mir_dataflow::{self, Analysis};
|
||||
use rustc_span::{sym, Span, Symbol};
|
||||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::SelectionContext;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{
|
||||
self, ObligationCauseCode, SelectionContext, TraitEngine, TraitEngineExt,
|
||||
};
|
||||
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
@ -738,6 +741,43 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
selcx.select(&obligation)
|
||||
});
|
||||
|
||||
// do a well-formedness check on the trait method being called. This is because typeck only does a
|
||||
// "non-const" check. This is required for correctness here.
|
||||
tcx.infer_ctxt().enter(|infcx| {
|
||||
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
|
||||
let predicates = tcx.predicates_of(callee).instantiate(tcx, substs);
|
||||
let hir_id = tcx
|
||||
.hir()
|
||||
.local_def_id_to_hir_id(self.body.source.def_id().expect_local());
|
||||
let cause = || {
|
||||
ObligationCause::new(
|
||||
terminator.source_info.span,
|
||||
hir_id,
|
||||
ObligationCauseCode::ItemObligation(callee),
|
||||
)
|
||||
};
|
||||
let normalized = infcx.partially_normalize_associated_types_in(
|
||||
cause(),
|
||||
param_env,
|
||||
predicates,
|
||||
);
|
||||
|
||||
for p in normalized.obligations {
|
||||
fulfill_cx.register_predicate_obligation(&infcx, p);
|
||||
}
|
||||
for obligation in traits::predicates_for_generics(
|
||||
|_, _| cause(),
|
||||
self.param_env,
|
||||
normalized.value,
|
||||
) {
|
||||
fulfill_cx.register_predicate_obligation(&infcx, obligation);
|
||||
}
|
||||
let errors = fulfill_cx.select_all_or_error(&infcx);
|
||||
if !errors.is_empty() {
|
||||
infcx.report_fulfillment_errors(&errors, None, false);
|
||||
}
|
||||
});
|
||||
|
||||
match implsrc {
|
||||
Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
|
||||
debug!(
|
||||
|
@ -1410,7 +1410,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, code, span, def_id, substs))]
|
||||
#[instrument(level = "debug", skip(self, code, span, substs))]
|
||||
fn add_required_obligations_with_code(
|
||||
&self,
|
||||
span: Span,
|
||||
@ -1418,14 +1418,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
substs: SubstsRef<'tcx>,
|
||||
code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>,
|
||||
) {
|
||||
// Associated consts have `Self: ~const Trait` bounds that should be satisfiable when
|
||||
// `Self: Trait` is satisfied because it does not matter whether the impl is `const`.
|
||||
// Therefore we have to remap the param env here to be non-const.
|
||||
let param_env = if let hir::def::DefKind::AssocConst = self.tcx.def_kind(def_id) {
|
||||
self.param_env.without_const()
|
||||
} else {
|
||||
self.param_env
|
||||
};
|
||||
let mut param_env = self.param_env;
|
||||
match self.tcx.def_kind(def_id) {
|
||||
// Associated consts have `Self: ~const Trait` bounds that should be satisfiable when
|
||||
// `Self: Trait` is satisfied because it does not matter whether the impl is `const`.
|
||||
// Therefore we have to remap the param env here to be non-const.
|
||||
hir::def::DefKind::AssocConst => param_env = param_env.without_const(),
|
||||
hir::def::DefKind::AssocFn
|
||||
if self.tcx.def_kind(self.tcx.parent(def_id)) == hir::def::DefKind::Trait =>
|
||||
{
|
||||
// N.B.: All callsites to this function involve checking a path expression.
|
||||
//
|
||||
// When instantiating a trait method as a function item, it does not actually matter whether
|
||||
// the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied as
|
||||
// `const`. If we were to introduce instantiating trait methods as `const fn`s, we would
|
||||
// check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a
|
||||
// `const fn` pointer.
|
||||
//
|
||||
// FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy
|
||||
// `~const FnOnce` or can be coerced to `const fn` pointer.
|
||||
param_env = param_env.without_const();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);
|
||||
|
||||
for obligation in traits::predicates_for_generics(
|
||||
|
@ -0,0 +1,18 @@
|
||||
// check-pass
|
||||
pub struct S<T, F: FnOnce() -> T = fn() -> T> {
|
||||
f: F,
|
||||
x: Option<T>,
|
||||
}
|
||||
|
||||
impl<T, F: FnOnce() -> T> S<T, F> {
|
||||
pub const fn new(f: F) -> Self {
|
||||
Self { f, x: None }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Foo;
|
||||
|
||||
static LOCKED_CALLSITES: S<Foo> = S::new(Default::default);
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,29 @@
|
||||
// Like trait-where-clause.rs, but we are calling from a const context.
|
||||
// Checking the validity of traits' where clauses happen at a later stage.
|
||||
// (`rustc_const_eval` instead of `rustc_typeck`) Therefore one file as a
|
||||
// test is not enough.
|
||||
#![feature(const_trait_impl)]
|
||||
|
||||
trait Bar {}
|
||||
|
||||
trait Foo {
|
||||
fn a();
|
||||
fn b() where Self: ~const Bar;
|
||||
fn c<T: ~const Bar>();
|
||||
}
|
||||
|
||||
const fn test1<T: ~const Foo + Bar>() {
|
||||
T::a();
|
||||
T::b();
|
||||
//~^ ERROR the trait bound
|
||||
T::c::<T>();
|
||||
//~^ ERROR the trait bound
|
||||
}
|
||||
|
||||
const fn test2<T: ~const Foo + ~const Bar>() {
|
||||
T::a();
|
||||
T::b();
|
||||
T::c::<T>();
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,35 @@
|
||||
error[E0277]: the trait bound `T: ~const Bar` is not satisfied
|
||||
--> $DIR/trait-where-clause-const.rs:17:5
|
||||
|
|
||||
LL | T::b();
|
||||
| ^^^^^^ the trait `~const Bar` is not implemented for `T`
|
||||
|
|
||||
note: the trait `Bar` is implemented for `T`, but that implementation is not `const`
|
||||
--> $DIR/trait-where-clause-const.rs:17:5
|
||||
|
|
||||
LL | T::b();
|
||||
| ^^^^^^
|
||||
help: consider further restricting this bound
|
||||
|
|
||||
LL | const fn test1<T: ~const Foo + Bar + ~const Bar>() {
|
||||
| ++++++++++++
|
||||
|
||||
error[E0277]: the trait bound `T: ~const Bar` is not satisfied
|
||||
--> $DIR/trait-where-clause-const.rs:19:5
|
||||
|
|
||||
LL | T::c::<T>();
|
||||
| ^^^^^^^^^^^ the trait `~const Bar` is not implemented for `T`
|
||||
|
|
||||
note: the trait `Bar` is implemented for `T`, but that implementation is not `const`
|
||||
--> $DIR/trait-where-clause-const.rs:19:5
|
||||
|
|
||||
LL | T::c::<T>();
|
||||
| ^^^^^^^^^^^
|
||||
help: consider further restricting this bound
|
||||
|
|
||||
LL | const fn test1<T: ~const Foo + Bar + ~const Bar>() {
|
||||
| ++++++++++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
@ -8,7 +8,7 @@ trait Foo {
|
||||
fn c<T: ~const Bar>();
|
||||
}
|
||||
|
||||
const fn test1<T: ~const Foo + Bar>() {
|
||||
fn test1<T: Foo>() {
|
||||
T::a();
|
||||
T::b();
|
||||
//~^ ERROR the trait bound
|
||||
@ -16,21 +16,7 @@ const fn test1<T: ~const Foo + Bar>() {
|
||||
//~^ ERROR the trait bound
|
||||
}
|
||||
|
||||
const fn test2<T: ~const Foo + ~const Bar>() {
|
||||
T::a();
|
||||
T::b();
|
||||
T::c::<T>();
|
||||
}
|
||||
|
||||
fn test3<T: Foo>() {
|
||||
T::a();
|
||||
T::b();
|
||||
//~^ ERROR the trait bound
|
||||
T::c::<T>();
|
||||
//~^ ERROR the trait bound
|
||||
}
|
||||
|
||||
fn test4<T: Foo + Bar>() {
|
||||
fn test2<T: Foo + Bar>() {
|
||||
T::a();
|
||||
T::b();
|
||||
T::c::<T>();
|
||||
|
@ -1,47 +1,5 @@
|
||||
error[E0277]: the trait bound `T: ~const Bar` is not satisfied
|
||||
--> $DIR/trait-where-clause.rs:13:5
|
||||
|
|
||||
LL | T::b();
|
||||
| ^^^^ the trait `~const Bar` is not implemented for `T`
|
||||
|
|
||||
note: the trait `Bar` is implemented for `T`, but that implementation is not `const`
|
||||
--> $DIR/trait-where-clause.rs:13:5
|
||||
|
|
||||
LL | T::b();
|
||||
| ^^^^
|
||||
note: required by a bound in `Foo::b`
|
||||
--> $DIR/trait-where-clause.rs:7:24
|
||||
|
|
||||
LL | fn b() where Self: ~const Bar;
|
||||
| ^^^^^^^^^^ required by this bound in `Foo::b`
|
||||
help: consider further restricting this bound
|
||||
|
|
||||
LL | const fn test1<T: ~const Foo + Bar + ~const Bar>() {
|
||||
| ++++++++++++
|
||||
|
||||
error[E0277]: the trait bound `T: ~const Bar` is not satisfied
|
||||
--> $DIR/trait-where-clause.rs:15:12
|
||||
|
|
||||
LL | T::c::<T>();
|
||||
| ^ the trait `~const Bar` is not implemented for `T`
|
||||
|
|
||||
note: the trait `Bar` is implemented for `T`, but that implementation is not `const`
|
||||
--> $DIR/trait-where-clause.rs:15:12
|
||||
|
|
||||
LL | T::c::<T>();
|
||||
| ^
|
||||
note: required by a bound in `Foo::c`
|
||||
--> $DIR/trait-where-clause.rs:8:13
|
||||
|
|
||||
LL | fn c<T: ~const Bar>();
|
||||
| ^^^^^^^^^^ required by this bound in `Foo::c`
|
||||
help: consider further restricting this bound
|
||||
|
|
||||
LL | const fn test1<T: ~const Foo + Bar + ~const Bar>() {
|
||||
| ++++++++++++
|
||||
|
||||
error[E0277]: the trait bound `T: Bar` is not satisfied
|
||||
--> $DIR/trait-where-clause.rs:27:5
|
||||
--> $DIR/trait-where-clause.rs:13:5
|
||||
|
|
||||
LL | T::b();
|
||||
| ^^^^ the trait `Bar` is not implemented for `T`
|
||||
@ -53,11 +11,11 @@ LL | fn b() where Self: ~const Bar;
|
||||
| ^^^^^^^^^^ required by this bound in `Foo::b`
|
||||
help: consider further restricting this bound
|
||||
|
|
||||
LL | fn test3<T: Foo + Bar>() {
|
||||
LL | fn test1<T: Foo + Bar>() {
|
||||
| +++++
|
||||
|
||||
error[E0277]: the trait bound `T: Bar` is not satisfied
|
||||
--> $DIR/trait-where-clause.rs:29:12
|
||||
--> $DIR/trait-where-clause.rs:15:12
|
||||
|
|
||||
LL | T::c::<T>();
|
||||
| ^ the trait `Bar` is not implemented for `T`
|
||||
@ -69,9 +27,9 @@ LL | fn c<T: ~const Bar>();
|
||||
| ^^^^^^^^^^ required by this bound in `Foo::c`
|
||||
help: consider further restricting this bound
|
||||
|
|
||||
LL | fn test3<T: Foo + Bar>() {
|
||||
LL | fn test1<T: Foo + Bar>() {
|
||||
| +++++
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
Loading…
x
Reference in New Issue
Block a user