about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDeadbeef <ent3rm4n@gmail.com>2022-09-10 06:37:26 +0000
committerDeadbeef <ent3rm4n@gmail.com>2022-09-16 11:48:43 +0800
commitf8813cf10eea79f7703e716b6c050e818220670a (patch)
tree3784e8db8807e9cd46581ed9a494b41d0f38f5ea
parent1bcc26a6b135fb934bf5f91d03d525f204323829 (diff)
downloadrust-f8813cf10eea79f7703e716b6c050e818220670a.tar.gz
rust-f8813cf10eea79f7703e716b6c050e818220670a.zip
do const trait method bounds check later in rustc_const_eval
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs19
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs44
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs33
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/static-const-trait-bound.rs18
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-const.rs29
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-const.stderr35
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.rs18
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr52
8 files changed, 174 insertions, 74 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index f41d70d384d..79e7eb0f1cc 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -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;
+                }
             }
         }
     }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index 7e15858c8c1..329d1033abf 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -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!(
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index a7b4d08cbf3..31eb2b65476 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -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(
diff --git a/src/test/ui/rfc-2632-const-trait-impl/static-const-trait-bound.rs b/src/test/ui/rfc-2632-const-trait-impl/static-const-trait-bound.rs
new file mode 100644
index 00000000000..4520a36960c
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/static-const-trait-bound.rs
@@ -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() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-const.rs b/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-const.rs
new file mode 100644
index 00000000000..7a88ec35c8f
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-const.rs
@@ -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() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-const.stderr b/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-const.stderr
new file mode 100644
index 00000000000..13d8639de30
--- /dev/null
+++ b/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-const.stderr
@@ -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`.
diff --git a/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.rs b/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.rs
index d37ed3bb8dd..5bd23a8cb20 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.rs
@@ -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>();
diff --git a/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr b/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr
index fd5fe25ddcf..96365d33433 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr
@@ -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`.