Skip to content

Commit

Permalink
Implement Generator and Future
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Jan 26, 2023
1 parent 8434b43 commit d600b94
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 1 deletion.
14 changes: 14 additions & 0 deletions compiler/rustc_trait_selection/src/solve/assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
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> {
Expand Down Expand Up @@ -266,6 +276,10 @@ impl<'tcx> EvalCtxt<'_, '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)
};
Expand Down
69 changes: 68 additions & 1 deletion compiler/rustc_trait_selection/src/solve/project_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
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;

Expand Down Expand Up @@ -482,6 +482,73 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
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.
Expand Down
44 changes: 44 additions & 0 deletions compiler/rustc_trait_selection/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,50 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
) -> 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> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
17 changes: 17 additions & 0 deletions tests/ui/traits/new-solver/async.fail.stderr
Original file line number Diff line number Diff line change
@@ -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`.
19 changes: 19 additions & 0 deletions tests/ui/traits/new-solver/async.rs
Original file line number Diff line number Diff line change
@@ -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 });
}
64 changes: 64 additions & 0 deletions tests/ui/traits/new-solver/generator.fail.stderr
Original file line number Diff line number Diff line change
@@ -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`.
32 changes: 32 additions & 0 deletions tests/ui/traits/new-solver/generator.rs
Original file line number Diff line number Diff line change
@@ -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
})
}

0 comments on commit d600b94

Please sign in to comment.