do const trait method bounds check later in rustc_const_eval
This commit is contained in:
parent
1bcc26a6b1
commit
f8813cf10e
8 changed files with 174 additions and 74 deletions
|
@ -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() {
|
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 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(
|
self.cx.normalize_and_prove_instantiated_predicates(
|
||||||
def_id,
|
def_id,
|
||||||
instantiated_predicates,
|
instantiated_predicates,
|
||||||
locations,
|
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_middle::ty::{Binder, TraitPredicate, TraitRef, TypeVisitable};
|
||||||
use rustc_mir_dataflow::{self, Analysis};
|
use rustc_mir_dataflow::{self, Analysis};
|
||||||
use rustc_span::{sym, Span, Symbol};
|
use rustc_span::{sym, Span, Symbol};
|
||||||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
|
use rustc_trait_selection::infer::InferCtxtExt;
|
||||||
use rustc_trait_selection::traits::SelectionContext;
|
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
||||||
|
use rustc_trait_selection::traits::{
|
||||||
|
self, ObligationCauseCode, SelectionContext, TraitEngine, TraitEngineExt,
|
||||||
|
};
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -738,6 +741,43 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||||
selcx.select(&obligation)
|
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 {
|
match implsrc {
|
||||||
Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
|
Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
|
||||||
debug!(
|
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(
|
fn add_required_obligations_with_code(
|
||||||
&self,
|
&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
@ -1418,14 +1418,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
substs: SubstsRef<'tcx>,
|
substs: SubstsRef<'tcx>,
|
||||||
code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>,
|
code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>,
|
||||||
) {
|
) {
|
||||||
// Associated consts have `Self: ~const Trait` bounds that should be satisfiable when
|
let mut param_env = self.param_env;
|
||||||
// `Self: Trait` is satisfied because it does not matter whether the impl is `const`.
|
match self.tcx.def_kind(def_id) {
|
||||||
// Therefore we have to remap the param env here to be non-const.
|
// Associated consts have `Self: ~const Trait` bounds that should be satisfiable when
|
||||||
let param_env = if let hir::def::DefKind::AssocConst = self.tcx.def_kind(def_id) {
|
// `Self: Trait` is satisfied because it does not matter whether the impl is `const`.
|
||||||
self.param_env.without_const()
|
// Therefore we have to remap the param env here to be non-const.
|
||||||
} else {
|
hir::def::DefKind::AssocConst => param_env = param_env.without_const(),
|
||||||
self.param_env
|
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);
|
let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);
|
||||||
|
|
||||||
for obligation in traits::predicates_for_generics(
|
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>();
|
fn c<T: ~const Bar>();
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn test1<T: ~const Foo + Bar>() {
|
fn test1<T: Foo>() {
|
||||||
T::a();
|
T::a();
|
||||||
T::b();
|
T::b();
|
||||||
//~^ ERROR the trait bound
|
//~^ ERROR the trait bound
|
||||||
|
@ -16,21 +16,7 @@ const fn test1<T: ~const Foo + Bar>() {
|
||||||
//~^ ERROR the trait bound
|
//~^ ERROR the trait bound
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn test2<T: ~const Foo + ~const Bar>() {
|
fn test2<T: Foo + 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>() {
|
|
||||||
T::a();
|
T::a();
|
||||||
T::b();
|
T::b();
|
||||||
T::c::<T>();
|
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
|
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();
|
LL | T::b();
|
||||||
| ^^^^ the trait `Bar` is not implemented for `T`
|
| ^^^^ 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`
|
| ^^^^^^^^^^ required by this bound in `Foo::b`
|
||||||
help: consider further restricting this bound
|
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
|
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>();
|
LL | T::c::<T>();
|
||||||
| ^ the trait `Bar` is not implemented for `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`
|
| ^^^^^^^^^^ required by this bound in `Foo::c`
|
||||||
help: consider further restricting this bound
|
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`.
|
For more information about this error, try `rustc --explain E0277`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue