Skip to content

Commit f8813cf

Browse files
committed
do const trait method bounds check later in rustc_const_eval
1 parent 1bcc26a commit f8813cf

File tree

8 files changed

+174
-74
lines changed

8 files changed

+174
-74
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,12 +424,31 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
424424
}
425425

426426
if let ty::FnDef(def_id, substs) = *constant.literal.ty().kind() {
427+
// N.B.: When instantiating a trait method as a function item, it does not actually matter
428+
// whether the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied
429+
// as `const`. If we were to introduce instantiating trait methods as `const fn`s, we would
430+
// check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a
431+
// `const fn` pointer.
432+
//
433+
// FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy
434+
// `~const FnOnce` or can be coerced to `const fn` pointer.
435+
let const_norm = self.tcx().def_kind(def_id) == hir::def::DefKind::AssocFn
436+
&& self.tcx().def_kind(ty::DefIdTree::parent(self.tcx(), def_id))
437+
== hir::def::DefKind::Trait;
438+
427439
let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
440+
let prev = self.cx.param_env;
441+
if const_norm {
442+
self.cx.param_env = prev.without_const();
443+
}
428444
self.cx.normalize_and_prove_instantiated_predicates(
429445
def_id,
430446
instantiated_predicates,
431447
locations,
432448
);
449+
if const_norm {
450+
self.cx.param_env = prev;
451+
}
433452
}
434453
}
435454
}

compiler/rustc_const_eval/src/transform/check_consts/check.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty,
1313
use rustc_middle::ty::{Binder, TraitPredicate, TraitRef, TypeVisitable};
1414
use rustc_mir_dataflow::{self, Analysis};
1515
use rustc_span::{sym, Span, Symbol};
16-
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
17-
use rustc_trait_selection::traits::SelectionContext;
16+
use rustc_trait_selection::infer::InferCtxtExt;
17+
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
18+
use rustc_trait_selection::traits::{
19+
self, ObligationCauseCode, SelectionContext, TraitEngine, TraitEngineExt,
20+
};
1821

1922
use std::mem;
2023
use std::ops::Deref;
@@ -738,6 +741,43 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
738741
selcx.select(&obligation)
739742
});
740743

744+
// do a well-formedness check on the trait method being called. This is because typeck only does a
745+
// "non-const" check. This is required for correctness here.
746+
tcx.infer_ctxt().enter(|infcx| {
747+
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
748+
let predicates = tcx.predicates_of(callee).instantiate(tcx, substs);
749+
let hir_id = tcx
750+
.hir()
751+
.local_def_id_to_hir_id(self.body.source.def_id().expect_local());
752+
let cause = || {
753+
ObligationCause::new(
754+
terminator.source_info.span,
755+
hir_id,
756+
ObligationCauseCode::ItemObligation(callee),
757+
)
758+
};
759+
let normalized = infcx.partially_normalize_associated_types_in(
760+
cause(),
761+
param_env,
762+
predicates,
763+
);
764+
765+
for p in normalized.obligations {
766+
fulfill_cx.register_predicate_obligation(&infcx, p);
767+
}
768+
for obligation in traits::predicates_for_generics(
769+
|_, _| cause(),
770+
self.param_env,
771+
normalized.value,
772+
) {
773+
fulfill_cx.register_predicate_obligation(&infcx, obligation);
774+
}
775+
let errors = fulfill_cx.select_all_or_error(&infcx);
776+
if !errors.is_empty() {
777+
infcx.report_fulfillment_errors(&errors, None, false);
778+
}
779+
});
780+
741781
match implsrc {
742782
Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
743783
debug!(

compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,22 +1410,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14101410
})
14111411
}
14121412

1413-
#[instrument(level = "debug", skip(self, code, span, def_id, substs))]
1413+
#[instrument(level = "debug", skip(self, code, span, substs))]
14141414
fn add_required_obligations_with_code(
14151415
&self,
14161416
span: Span,
14171417
def_id: DefId,
14181418
substs: SubstsRef<'tcx>,
14191419
code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>,
14201420
) {
1421-
// Associated consts have `Self: ~const Trait` bounds that should be satisfiable when
1422-
// `Self: Trait` is satisfied because it does not matter whether the impl is `const`.
1423-
// Therefore we have to remap the param env here to be non-const.
1424-
let param_env = if let hir::def::DefKind::AssocConst = self.tcx.def_kind(def_id) {
1425-
self.param_env.without_const()
1426-
} else {
1427-
self.param_env
1428-
};
1421+
let mut param_env = self.param_env;
1422+
match self.tcx.def_kind(def_id) {
1423+
// Associated consts have `Self: ~const Trait` bounds that should be satisfiable when
1424+
// `Self: Trait` is satisfied because it does not matter whether the impl is `const`.
1425+
// Therefore we have to remap the param env here to be non-const.
1426+
hir::def::DefKind::AssocConst => param_env = param_env.without_const(),
1427+
hir::def::DefKind::AssocFn
1428+
if self.tcx.def_kind(self.tcx.parent(def_id)) == hir::def::DefKind::Trait =>
1429+
{
1430+
// N.B.: All callsites to this function involve checking a path expression.
1431+
//
1432+
// When instantiating a trait method as a function item, it does not actually matter whether
1433+
// the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied as
1434+
// `const`. If we were to introduce instantiating trait methods as `const fn`s, we would
1435+
// check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a
1436+
// `const fn` pointer.
1437+
//
1438+
// FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy
1439+
// `~const FnOnce` or can be coerced to `const fn` pointer.
1440+
param_env = param_env.without_const();
1441+
}
1442+
_ => {}
1443+
}
14291444
let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);
14301445

14311446
for obligation in traits::predicates_for_generics(
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// check-pass
2+
pub struct S<T, F: FnOnce() -> T = fn() -> T> {
3+
f: F,
4+
x: Option<T>,
5+
}
6+
7+
impl<T, F: FnOnce() -> T> S<T, F> {
8+
pub const fn new(f: F) -> Self {
9+
Self { f, x: None }
10+
}
11+
}
12+
13+
#[derive(Default)]
14+
pub struct Foo;
15+
16+
static LOCKED_CALLSITES: S<Foo> = S::new(Default::default);
17+
18+
fn main() {}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Like trait-where-clause.rs, but we are calling from a const context.
2+
// Checking the validity of traits' where clauses happen at a later stage.
3+
// (`rustc_const_eval` instead of `rustc_typeck`) Therefore one file as a
4+
// test is not enough.
5+
#![feature(const_trait_impl)]
6+
7+
trait Bar {}
8+
9+
trait Foo {
10+
fn a();
11+
fn b() where Self: ~const Bar;
12+
fn c<T: ~const Bar>();
13+
}
14+
15+
const fn test1<T: ~const Foo + Bar>() {
16+
T::a();
17+
T::b();
18+
//~^ ERROR the trait bound
19+
T::c::<T>();
20+
//~^ ERROR the trait bound
21+
}
22+
23+
const fn test2<T: ~const Foo + ~const Bar>() {
24+
T::a();
25+
T::b();
26+
T::c::<T>();
27+
}
28+
29+
fn main() {}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error[E0277]: the trait bound `T: ~const Bar` is not satisfied
2+
--> $DIR/trait-where-clause-const.rs:17:5
3+
|
4+
LL | T::b();
5+
| ^^^^^^ the trait `~const Bar` is not implemented for `T`
6+
|
7+
note: the trait `Bar` is implemented for `T`, but that implementation is not `const`
8+
--> $DIR/trait-where-clause-const.rs:17:5
9+
|
10+
LL | T::b();
11+
| ^^^^^^
12+
help: consider further restricting this bound
13+
|
14+
LL | const fn test1<T: ~const Foo + Bar + ~const Bar>() {
15+
| ++++++++++++
16+
17+
error[E0277]: the trait bound `T: ~const Bar` is not satisfied
18+
--> $DIR/trait-where-clause-const.rs:19:5
19+
|
20+
LL | T::c::<T>();
21+
| ^^^^^^^^^^^ the trait `~const Bar` is not implemented for `T`
22+
|
23+
note: the trait `Bar` is implemented for `T`, but that implementation is not `const`
24+
--> $DIR/trait-where-clause-const.rs:19:5
25+
|
26+
LL | T::c::<T>();
27+
| ^^^^^^^^^^^
28+
help: consider further restricting this bound
29+
|
30+
LL | const fn test1<T: ~const Foo + Bar + ~const Bar>() {
31+
| ++++++++++++
32+
33+
error: aborting due to 2 previous errors
34+
35+
For more information about this error, try `rustc --explain E0277`.

src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,15 @@ trait Foo {
88
fn c<T: ~const Bar>();
99
}
1010

11-
const fn test1<T: ~const Foo + Bar>() {
11+
fn test1<T: Foo>() {
1212
T::a();
1313
T::b();
1414
//~^ ERROR the trait bound
1515
T::c::<T>();
1616
//~^ ERROR the trait bound
1717
}
1818

19-
const fn test2<T: ~const Foo + ~const Bar>() {
20-
T::a();
21-
T::b();
22-
T::c::<T>();
23-
}
24-
25-
fn test3<T: Foo>() {
26-
T::a();
27-
T::b();
28-
//~^ ERROR the trait bound
29-
T::c::<T>();
30-
//~^ ERROR the trait bound
31-
}
32-
33-
fn test4<T: Foo + Bar>() {
19+
fn test2<T: Foo + Bar>() {
3420
T::a();
3521
T::b();
3622
T::c::<T>();
Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,5 @@
1-
error[E0277]: the trait bound `T: ~const Bar` is not satisfied
2-
--> $DIR/trait-where-clause.rs:13:5
3-
|
4-
LL | T::b();
5-
| ^^^^ the trait `~const Bar` is not implemented for `T`
6-
|
7-
note: the trait `Bar` is implemented for `T`, but that implementation is not `const`
8-
--> $DIR/trait-where-clause.rs:13:5
9-
|
10-
LL | T::b();
11-
| ^^^^
12-
note: required by a bound in `Foo::b`
13-
--> $DIR/trait-where-clause.rs:7:24
14-
|
15-
LL | fn b() where Self: ~const Bar;
16-
| ^^^^^^^^^^ required by this bound in `Foo::b`
17-
help: consider further restricting this bound
18-
|
19-
LL | const fn test1<T: ~const Foo + Bar + ~const Bar>() {
20-
| ++++++++++++
21-
22-
error[E0277]: the trait bound `T: ~const Bar` is not satisfied
23-
--> $DIR/trait-where-clause.rs:15:12
24-
|
25-
LL | T::c::<T>();
26-
| ^ the trait `~const Bar` is not implemented for `T`
27-
|
28-
note: the trait `Bar` is implemented for `T`, but that implementation is not `const`
29-
--> $DIR/trait-where-clause.rs:15:12
30-
|
31-
LL | T::c::<T>();
32-
| ^
33-
note: required by a bound in `Foo::c`
34-
--> $DIR/trait-where-clause.rs:8:13
35-
|
36-
LL | fn c<T: ~const Bar>();
37-
| ^^^^^^^^^^ required by this bound in `Foo::c`
38-
help: consider further restricting this bound
39-
|
40-
LL | const fn test1<T: ~const Foo + Bar + ~const Bar>() {
41-
| ++++++++++++
42-
431
error[E0277]: the trait bound `T: Bar` is not satisfied
44-
--> $DIR/trait-where-clause.rs:27:5
2+
--> $DIR/trait-where-clause.rs:13:5
453
|
464
LL | T::b();
475
| ^^^^ the trait `Bar` is not implemented for `T`
@@ -53,11 +11,11 @@ LL | fn b() where Self: ~const Bar;
5311
| ^^^^^^^^^^ required by this bound in `Foo::b`
5412
help: consider further restricting this bound
5513
|
56-
LL | fn test3<T: Foo + Bar>() {
14+
LL | fn test1<T: Foo + Bar>() {
5715
| +++++
5816

5917
error[E0277]: the trait bound `T: Bar` is not satisfied
60-
--> $DIR/trait-where-clause.rs:29:12
18+
--> $DIR/trait-where-clause.rs:15:12
6119
|
6220
LL | T::c::<T>();
6321
| ^ the trait `Bar` is not implemented for `T`
@@ -69,9 +27,9 @@ LL | fn c<T: ~const Bar>();
6927
| ^^^^^^^^^^ required by this bound in `Foo::c`
7028
help: consider further restricting this bound
7129
|
72-
LL | fn test3<T: Foo + Bar>() {
30+
LL | fn test1<T: Foo + Bar>() {
7331
| +++++
7432

75-
error: aborting due to 4 previous errors
33+
error: aborting due to 2 previous errors
7634

7735
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)