]> git.lizzy.rs Git - rust.git/commitdiff
Implement Generator and Future
authorMichael Goulet <michael@errs.io>
Tue, 24 Jan 2023 23:38:20 +0000 (23:38 +0000)
committerMichael Goulet <michael@errs.io>
Thu, 26 Jan 2023 03:15:36 +0000 (03:15 +0000)
compiler/rustc_trait_selection/src/solve/assembly.rs
compiler/rustc_trait_selection/src/solve/project_goals.rs
compiler/rustc_trait_selection/src/solve/trait_goals.rs
compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs
tests/ui/traits/new-solver/async.fail.stderr [new file with mode: 0644]
tests/ui/traits/new-solver/async.rs [new file with mode: 0644]
tests/ui/traits/new-solver/generator.fail.stderr [new file with mode: 0644]
tests/ui/traits/new-solver/generator.rs [new file with mode: 0644]

index 0b642fcba281249da01b21d4af4ece7fe96c2636..baff0f8630ee56ce01fcf2665d8e7626fa3cc283 100644 (file)
@@ -138,6 +138,16 @@ fn consider_builtin_pointee_candidate(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
+
+    fn consider_builtin_future_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
+
+    fn consider_builtin_generator_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx>;
 }
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
@@ -266,6 +276,10 @@ fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
             G::consider_builtin_tuple_candidate(self, goal)
         } else if lang_items.pointee_trait() == Some(trait_def_id) {
             G::consider_builtin_pointee_candidate(self, goal)
+        } else if lang_items.future_trait() == Some(trait_def_id) {
+            G::consider_builtin_future_candidate(self, goal)
+        } else if lang_items.gen_trait() == Some(trait_def_id) {
+            G::consider_builtin_generator_candidate(self, goal)
         } else {
             Err(NoSolution)
         };
index e5072d8e2d15216adfb84e40a508470ddaaaef8d..4a9b95af4a27cbc8f20618c10f8099a5e5367106 100644 (file)
@@ -16,7 +16,7 @@
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
 use rustc_middle::ty::{ToPredicate, TypeVisitable};
-use rustc_span::DUMMY_SP;
+use rustc_span::{sym, DUMMY_SP};
 use std::iter;
 use std::ops::ControlFlow;
 
@@ -482,6 +482,73 @@ fn consider_builtin_pointee_candidate(
             ecx.evaluate_all_and_make_canonical_response(nested_goals)
         })
     }
+
+    fn consider_builtin_future_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let self_ty = goal.predicate.self_ty();
+        let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
+            return Err(NoSolution);
+        };
+
+        // Generators are not futures unless they come from `async` desugaring
+        let tcx = ecx.tcx();
+        if !tcx.generator_is_async(def_id) {
+            return Err(NoSolution);
+        }
+
+        let term = substs.as_generator().return_ty().into();
+
+        Self::consider_assumption(
+            ecx,
+            goal,
+            ty::Binder::dummy(ty::ProjectionPredicate {
+                projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]),
+                term,
+            })
+            .to_predicate(tcx),
+        )
+    }
+
+    fn consider_builtin_generator_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let self_ty = goal.predicate.self_ty();
+        let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
+            return Err(NoSolution);
+        };
+
+        // `async`-desugared generators do not implement the generator trait
+        let tcx = ecx.tcx();
+        if tcx.generator_is_async(def_id) {
+            return Err(NoSolution);
+        }
+
+        let generator = substs.as_generator();
+
+        let name = tcx.associated_item(goal.predicate.def_id()).name;
+        let term = if name == sym::Return {
+            generator.return_ty().into()
+        } else if name == sym::Yield {
+            generator.yield_ty().into()
+        } else {
+            bug!("unexpected associated item `<{self_ty} as Generator>::{name}`")
+        };
+
+        Self::consider_assumption(
+            ecx,
+            goal,
+            ty::Binder::dummy(ty::ProjectionPredicate {
+                projection_ty: ecx
+                    .tcx()
+                    .mk_alias_ty(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
+                term,
+            })
+            .to_predicate(tcx),
+        )
+    }
 }
 
 /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
index 67bd2495665465d254ec83cc927ce42b0471c975..d74857dc4b4803bac6b418941d6e9122dd1fae3b 100644 (file)
@@ -192,6 +192,50 @@ fn consider_builtin_pointee_candidate(
     ) -> QueryResult<'tcx> {
         ecx.make_canonical_response(Certainty::Yes)
     }
+
+    fn consider_builtin_future_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let ty::Generator(def_id, _, _) = *goal.predicate.self_ty().kind() else {
+            return Err(NoSolution);
+        };
+
+        // Generators are not futures unless they come from `async` desugaring
+        let tcx = ecx.tcx();
+        if !tcx.generator_is_async(def_id) {
+            return Err(NoSolution);
+        }
+
+        // Async generator unconditionally implement `Future`
+        ecx.make_canonical_response(Certainty::Yes)
+    }
+
+    fn consider_builtin_generator_candidate(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, Self>,
+    ) -> QueryResult<'tcx> {
+        let self_ty = goal.predicate.self_ty();
+        let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
+            return Err(NoSolution);
+        };
+
+        // `async`-desugared generators do not implement the generator trait
+        let tcx = ecx.tcx();
+        if tcx.generator_is_async(def_id) {
+            return Err(NoSolution);
+        }
+
+        let generator = substs.as_generator();
+        Self::consider_assumption(
+            ecx,
+            goal,
+            ty::Binder::dummy(
+                tcx.mk_trait_ref(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
+            )
+            .to_predicate(tcx),
+        )
+    }
 }
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
index a11cd13cb0856b4df3ace3c1261baf9a69d9d4dc..162953180c75ce09717fb2c071edec930acac33f 100644 (file)
@@ -173,6 +173,7 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
     }
 }
 
+// Returns a binder of the tupled inputs types and output type from a builtin callable type.
 pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
     tcx: TyCtxt<'tcx>,
     self_ty: Ty<'tcx>,
diff --git a/tests/ui/traits/new-solver/async.fail.stderr b/tests/ui/traits/new-solver/async.fail.stderr
new file mode 100644 (file)
index 0000000..b395c23
--- /dev/null
@@ -0,0 +1,17 @@
+error[E0271]: expected `[async block@$DIR/async.rs:12:17: 12:25]` to be a future that resolves to `i32`, but it resolves to `()`
+  --> $DIR/async.rs:12:17
+   |
+LL |     needs_async(async {});
+   |     ----------- ^^^^^^^^ expected `i32`, found `()`
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `needs_async`
+  --> $DIR/async.rs:8:31
+   |
+LL | fn needs_async(_: impl Future<Output = i32>) {}
+   |                               ^^^^^^^^^^^^ required by this bound in `needs_async`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0271`.
diff --git a/tests/ui/traits/new-solver/async.rs b/tests/ui/traits/new-solver/async.rs
new file mode 100644 (file)
index 0000000..195cc35
--- /dev/null
@@ -0,0 +1,19 @@
+// compile-flags: -Ztrait-solver=next
+// edition: 2021
+// revisions: pass fail
+//[pass] check-pass
+
+use std::future::Future;
+
+fn needs_async(_: impl Future<Output = i32>) {}
+
+#[cfg(fail)]
+fn main() {
+    needs_async(async {});
+    //[fail]~^ ERROR to be a future that resolves to `i32`, but it resolves to `()`
+}
+
+#[cfg(pass)]
+fn main() {
+    needs_async(async { 1i32 });
+}
diff --git a/tests/ui/traits/new-solver/generator.fail.stderr b/tests/ui/traits/new-solver/generator.fail.stderr
new file mode 100644 (file)
index 0000000..d94d41e
--- /dev/null
@@ -0,0 +1,64 @@
+error[E0277]: the trait bound `[generator@$DIR/generator.rs:18:21: 18:23]: Generator<A>` is not satisfied
+  --> $DIR/generator.rs:18:21
+   |
+LL |       needs_generator(|| {
+   |  _____---------------_^
+   | |     |
+   | |     required by a bound introduced by this call
+LL | |
+LL | |
+LL | |
+LL | |         yield ();
+LL | |     });
+   | |_____^ the trait `Generator<A>` is not implemented for `[generator@$DIR/generator.rs:18:21: 18:23]`
+   |
+note: required by a bound in `needs_generator`
+  --> $DIR/generator.rs:14:28
+   |
+LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `needs_generator`
+
+error[E0271]: type mismatch resolving `<[generator@$DIR/generator.rs:18:21: 18:23] as Generator<A>>::Yield == B`
+  --> $DIR/generator.rs:18:21
+   |
+LL |       needs_generator(|| {
+   |  _____---------------_^
+   | |     |
+   | |     required by a bound introduced by this call
+LL | |
+LL | |
+LL | |
+LL | |         yield ();
+LL | |     });
+   | |_____^ types differ
+   |
+note: required by a bound in `needs_generator`
+  --> $DIR/generator.rs:14:41
+   |
+LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
+   |                                         ^^^^^^^^^ required by this bound in `needs_generator`
+
+error[E0271]: type mismatch resolving `<[generator@$DIR/generator.rs:18:21: 18:23] as Generator<A>>::Return == C`
+  --> $DIR/generator.rs:18:21
+   |
+LL |       needs_generator(|| {
+   |  _____---------------_^
+   | |     |
+   | |     required by a bound introduced by this call
+LL | |
+LL | |
+LL | |
+LL | |         yield ();
+LL | |     });
+   | |_____^ types differ
+   |
+note: required by a bound in `needs_generator`
+  --> $DIR/generator.rs:14:52
+   |
+LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
+   |                                                    ^^^^^^^^^^ required by this bound in `needs_generator`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0271, E0277.
+For more information about an error, try `rustc --explain E0271`.
diff --git a/tests/ui/traits/new-solver/generator.rs b/tests/ui/traits/new-solver/generator.rs
new file mode 100644 (file)
index 0000000..364373c
--- /dev/null
@@ -0,0 +1,32 @@
+// compile-flags: -Ztrait-solver=next
+// edition: 2021
+// revisions: pass fail
+//[pass] check-pass
+
+#![feature(generator_trait, generators)]
+
+use std::ops::Generator;
+
+struct A;
+struct B;
+struct C;
+
+fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
+
+#[cfg(fail)]
+fn main() {
+    needs_generator(|| {
+        //[fail]~^ ERROR Generator<A>` is not satisfied
+        //[fail]~| ERROR as Generator<A>>::Yield == B`
+        //[fail]~| ERROR as Generator<A>>::Return == C`
+        yield ();
+    });
+}
+
+#[cfg(pass)]
+fn main() {
+    needs_generator(|_: A| {
+        let _: A = yield B;
+        C
+    })
+}