Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Generator and Future in the new solver #107285

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
67 changes: 57 additions & 10 deletions compiler/rustc_trait_selection/src/solve/fulfill.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::mem;

use super::{Certainty, InferCtxtEvalExt};
use rustc_infer::{
infer::InferCtxt,
traits::{
query::NoSolution, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
SelectionError, TraitEngine,
},
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::{
query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
PredicateObligation, SelectionError, TraitEngine,
};
use rustc_middle::ty;
use rustc_middle::ty::error::{ExpectedFound, TypeError};

use super::{Certainty, InferCtxtEvalExt};

/// A trait engine using the new trait solver.
///
Expand Down Expand Up @@ -70,9 +71,55 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
Err(NoSolution) => {
errors.push(FulfillmentError {
obligation: obligation.clone(),
code: FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
),
code: match goal.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Projection(_)) => {
FulfillmentErrorCode::CodeProjectionError(
// FIXME: This could be a `Sorts` if the term is a type
MismatchedProjectionTypes { err: TypeError::Mismatch },
)
}
ty::PredicateKind::Subtype(pred) => {
let (a, b) = infcx.replace_bound_vars_with_placeholders(
goal.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(true, a, b);
FulfillmentErrorCode::CodeSubtypeError(
expected_found,
TypeError::Sorts(expected_found),
)
}
ty::PredicateKind::Coerce(pred) => {
let (a, b) = infcx.replace_bound_vars_with_placeholders(
goal.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(false, a, b);
FulfillmentErrorCode::CodeSubtypeError(
expected_found,
TypeError::Sorts(expected_found),
)
}
ty::PredicateKind::ConstEquate(a, b) => {
let (a, b) = infcx.replace_bound_vars_with_placeholders(
goal.predicate.kind().rebind((a, b)),
);
let expected_found = ExpectedFound::new(true, a, b);
FulfillmentErrorCode::CodeConstEquateError(
expected_found,
TypeError::ConstMismatch(expected_found),
)
}
ty::PredicateKind::Clause(_)
| ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(_, _, _)
| ty::PredicateKind::ConstEvaluatable(_)
| ty::PredicateKind::TypeWellFormedFromEnv(_)
| ty::PredicateKind::Ambiguous => {
FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
)
}
},
root_obligation: obligation,
});
continue;
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
})
}