Rollup merge of #107285 - compiler-errors:new-solver-future-and-generator, r=lcnr
Implement `Generator` and `Future` in the new solver r? `@lcnr`
This commit is contained in:
commit
5be2f51428
9 changed files with 316 additions and 11 deletions
|
@ -138,6 +138,16 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
|
||||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
goal: Goal<'tcx, Self>,
|
goal: Goal<'tcx, Self>,
|
||||||
) -> QueryResult<'tcx>;
|
) -> 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> {
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
|
@ -266,6 +276,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
G::consider_builtin_tuple_candidate(self, goal)
|
G::consider_builtin_tuple_candidate(self, goal)
|
||||||
} else if lang_items.pointee_trait() == Some(trait_def_id) {
|
} else if lang_items.pointee_trait() == Some(trait_def_id) {
|
||||||
G::consider_builtin_pointee_candidate(self, goal)
|
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 {
|
} else {
|
||||||
Err(NoSolution)
|
Err(NoSolution)
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use super::{Certainty, InferCtxtEvalExt};
|
use rustc_infer::infer::InferCtxt;
|
||||||
use rustc_infer::{
|
use rustc_infer::traits::{
|
||||||
infer::InferCtxt,
|
query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
|
||||||
traits::{
|
PredicateObligation, SelectionError, TraitEngine,
|
||||||
query::NoSolution, FulfillmentError, FulfillmentErrorCode, 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.
|
/// A trait engine using the new trait solver.
|
||||||
///
|
///
|
||||||
|
@ -70,9 +71,55 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
|
||||||
Err(NoSolution) => {
|
Err(NoSolution) => {
|
||||||
errors.push(FulfillmentError {
|
errors.push(FulfillmentError {
|
||||||
obligation: obligation.clone(),
|
obligation: obligation.clone(),
|
||||||
code: FulfillmentErrorCode::CodeSelectionError(
|
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,
|
SelectionError::Unimplemented,
|
||||||
),
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
root_obligation: obligation,
|
root_obligation: obligation,
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -16,7 +16,7 @@ use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
|
use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
|
||||||
use rustc_middle::ty::{ToPredicate, TypeVisitable};
|
use rustc_middle::ty::{ToPredicate, TypeVisitable};
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::{sym, DUMMY_SP};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
|
@ -482,6 +482,73 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
ecx.evaluate_all_and_make_canonical_response(nested_goals)
|
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.
|
/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
|
||||||
|
|
|
@ -192,6 +192,50 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
ecx.make_canonical_response(Certainty::Yes)
|
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> {
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
|
|
|
@ -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>(
|
pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
self_ty: Ty<'tcx>,
|
self_ty: Ty<'tcx>,
|
||||||
|
|
17
tests/ui/traits/new-solver/async.fail.stderr
Normal file
17
tests/ui/traits/new-solver/async.fail.stderr
Normal file
|
@ -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
tests/ui/traits/new-solver/async.rs
Normal file
19
tests/ui/traits/new-solver/async.rs
Normal file
|
@ -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
tests/ui/traits/new-solver/generator.fail.stderr
Normal file
64
tests/ui/traits/new-solver/generator.fail.stderr
Normal file
|
@ -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
tests/ui/traits/new-solver/generator.rs
Normal file
32
tests/ui/traits/new-solver/generator.rs
Normal file
|
@ -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
|
||||||
|
})
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue