Skip to content

Commit

Permalink
Rollup merge of #107285 - compiler-errors:new-solver-future-and-gener…
Browse files Browse the repository at this point in the history
…ator, r=lcnr

Implement `Generator` and `Future` in the new solver

r? `@lcnr`
  • Loading branch information
matthiaskrgr authored Jan 25, 2023
2 parents cfce51d + b5f893b commit efca03b
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 11 deletions.
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 @@ -133,6 +133,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 @@ -259,6 +269,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
G::consider_builtin_fn_trait_candidates(self, goal, kind)
} else if lang_items.tuple_trait() == Some(trait_def_id) {
G::consider_builtin_tuple_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 @@ -15,7 +15,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 @@ -391,6 +391,73 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
) -> QueryResult<'tcx> {
bug!("`Tuple` does not have an associated type: {:?}", goal);
}

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 @@ -185,6 +185,50 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
Err(NoSolution)
}
}

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 efca03b

Please sign in to comment.