Skip to content

Commit 5be2f51

Browse files
authored
Rollup merge of rust-lang#107285 - compiler-errors:new-solver-future-and-generator, r=lcnr
Implement `Generator` and `Future` in the new solver r? `@lcnr`
2 parents a84e060 + d600b94 commit 5be2f51

File tree

9 files changed

+316
-11
lines changed

9 files changed

+316
-11
lines changed

compiler/rustc_trait_selection/src/solve/assembly.rs

+14
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
138138
ecx: &mut EvalCtxt<'_, 'tcx>,
139139
goal: Goal<'tcx, Self>,
140140
) -> QueryResult<'tcx>;
141+
142+
fn consider_builtin_future_candidate(
143+
ecx: &mut EvalCtxt<'_, 'tcx>,
144+
goal: Goal<'tcx, Self>,
145+
) -> QueryResult<'tcx>;
146+
147+
fn consider_builtin_generator_candidate(
148+
ecx: &mut EvalCtxt<'_, 'tcx>,
149+
goal: Goal<'tcx, Self>,
150+
) -> QueryResult<'tcx>;
141151
}
142152

143153
impl<'tcx> EvalCtxt<'_, 'tcx> {
@@ -266,6 +276,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
266276
G::consider_builtin_tuple_candidate(self, goal)
267277
} else if lang_items.pointee_trait() == Some(trait_def_id) {
268278
G::consider_builtin_pointee_candidate(self, goal)
279+
} else if lang_items.future_trait() == Some(trait_def_id) {
280+
G::consider_builtin_future_candidate(self, goal)
281+
} else if lang_items.gen_trait() == Some(trait_def_id) {
282+
G::consider_builtin_generator_candidate(self, goal)
269283
} else {
270284
Err(NoSolution)
271285
};

compiler/rustc_trait_selection/src/solve/fulfill.rs

+57-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use std::mem;
22

3-
use super::{Certainty, InferCtxtEvalExt};
4-
use rustc_infer::{
5-
infer::InferCtxt,
6-
traits::{
7-
query::NoSolution, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
8-
SelectionError, TraitEngine,
9-
},
3+
use rustc_infer::infer::InferCtxt;
4+
use rustc_infer::traits::{
5+
query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
6+
PredicateObligation, SelectionError, TraitEngine,
107
};
8+
use rustc_middle::ty;
9+
use rustc_middle::ty::error::{ExpectedFound, TypeError};
10+
11+
use super::{Certainty, InferCtxtEvalExt};
1112

1213
/// A trait engine using the new trait solver.
1314
///
@@ -70,9 +71,55 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
7071
Err(NoSolution) => {
7172
errors.push(FulfillmentError {
7273
obligation: obligation.clone(),
73-
code: FulfillmentErrorCode::CodeSelectionError(
74-
SelectionError::Unimplemented,
75-
),
74+
code: match goal.predicate.kind().skip_binder() {
75+
ty::PredicateKind::Clause(ty::Clause::Projection(_)) => {
76+
FulfillmentErrorCode::CodeProjectionError(
77+
// FIXME: This could be a `Sorts` if the term is a type
78+
MismatchedProjectionTypes { err: TypeError::Mismatch },
79+
)
80+
}
81+
ty::PredicateKind::Subtype(pred) => {
82+
let (a, b) = infcx.replace_bound_vars_with_placeholders(
83+
goal.predicate.kind().rebind((pred.a, pred.b)),
84+
);
85+
let expected_found = ExpectedFound::new(true, a, b);
86+
FulfillmentErrorCode::CodeSubtypeError(
87+
expected_found,
88+
TypeError::Sorts(expected_found),
89+
)
90+
}
91+
ty::PredicateKind::Coerce(pred) => {
92+
let (a, b) = infcx.replace_bound_vars_with_placeholders(
93+
goal.predicate.kind().rebind((pred.a, pred.b)),
94+
);
95+
let expected_found = ExpectedFound::new(false, a, b);
96+
FulfillmentErrorCode::CodeSubtypeError(
97+
expected_found,
98+
TypeError::Sorts(expected_found),
99+
)
100+
}
101+
ty::PredicateKind::ConstEquate(a, b) => {
102+
let (a, b) = infcx.replace_bound_vars_with_placeholders(
103+
goal.predicate.kind().rebind((a, b)),
104+
);
105+
let expected_found = ExpectedFound::new(true, a, b);
106+
FulfillmentErrorCode::CodeConstEquateError(
107+
expected_found,
108+
TypeError::ConstMismatch(expected_found),
109+
)
110+
}
111+
ty::PredicateKind::Clause(_)
112+
| ty::PredicateKind::WellFormed(_)
113+
| ty::PredicateKind::ObjectSafe(_)
114+
| ty::PredicateKind::ClosureKind(_, _, _)
115+
| ty::PredicateKind::ConstEvaluatable(_)
116+
| ty::PredicateKind::TypeWellFormedFromEnv(_)
117+
| ty::PredicateKind::Ambiguous => {
118+
FulfillmentErrorCode::CodeSelectionError(
119+
SelectionError::Unimplemented,
120+
)
121+
}
122+
},
76123
root_obligation: obligation,
77124
});
78125
continue;

compiler/rustc_trait_selection/src/solve/project_goals.rs

+68-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
1616
use rustc_middle::ty::{self, Ty, TyCtxt};
1717
use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
1818
use rustc_middle::ty::{ToPredicate, TypeVisitable};
19-
use rustc_span::DUMMY_SP;
19+
use rustc_span::{sym, DUMMY_SP};
2020
use std::iter;
2121
use std::ops::ControlFlow;
2222

@@ -482,6 +482,73 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
482482
ecx.evaluate_all_and_make_canonical_response(nested_goals)
483483
})
484484
}
485+
486+
fn consider_builtin_future_candidate(
487+
ecx: &mut EvalCtxt<'_, 'tcx>,
488+
goal: Goal<'tcx, Self>,
489+
) -> QueryResult<'tcx> {
490+
let self_ty = goal.predicate.self_ty();
491+
let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
492+
return Err(NoSolution);
493+
};
494+
495+
// Generators are not futures unless they come from `async` desugaring
496+
let tcx = ecx.tcx();
497+
if !tcx.generator_is_async(def_id) {
498+
return Err(NoSolution);
499+
}
500+
501+
let term = substs.as_generator().return_ty().into();
502+
503+
Self::consider_assumption(
504+
ecx,
505+
goal,
506+
ty::Binder::dummy(ty::ProjectionPredicate {
507+
projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]),
508+
term,
509+
})
510+
.to_predicate(tcx),
511+
)
512+
}
513+
514+
fn consider_builtin_generator_candidate(
515+
ecx: &mut EvalCtxt<'_, 'tcx>,
516+
goal: Goal<'tcx, Self>,
517+
) -> QueryResult<'tcx> {
518+
let self_ty = goal.predicate.self_ty();
519+
let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
520+
return Err(NoSolution);
521+
};
522+
523+
// `async`-desugared generators do not implement the generator trait
524+
let tcx = ecx.tcx();
525+
if tcx.generator_is_async(def_id) {
526+
return Err(NoSolution);
527+
}
528+
529+
let generator = substs.as_generator();
530+
531+
let name = tcx.associated_item(goal.predicate.def_id()).name;
532+
let term = if name == sym::Return {
533+
generator.return_ty().into()
534+
} else if name == sym::Yield {
535+
generator.yield_ty().into()
536+
} else {
537+
bug!("unexpected associated item `<{self_ty} as Generator>::{name}`")
538+
};
539+
540+
Self::consider_assumption(
541+
ecx,
542+
goal,
543+
ty::Binder::dummy(ty::ProjectionPredicate {
544+
projection_ty: ecx
545+
.tcx()
546+
.mk_alias_ty(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
547+
term,
548+
})
549+
.to_predicate(tcx),
550+
)
551+
}
485552
}
486553

487554
/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+44
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,50 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
192192
) -> QueryResult<'tcx> {
193193
ecx.make_canonical_response(Certainty::Yes)
194194
}
195+
196+
fn consider_builtin_future_candidate(
197+
ecx: &mut EvalCtxt<'_, 'tcx>,
198+
goal: Goal<'tcx, Self>,
199+
) -> QueryResult<'tcx> {
200+
let ty::Generator(def_id, _, _) = *goal.predicate.self_ty().kind() else {
201+
return Err(NoSolution);
202+
};
203+
204+
// Generators are not futures unless they come from `async` desugaring
205+
let tcx = ecx.tcx();
206+
if !tcx.generator_is_async(def_id) {
207+
return Err(NoSolution);
208+
}
209+
210+
// Async generator unconditionally implement `Future`
211+
ecx.make_canonical_response(Certainty::Yes)
212+
}
213+
214+
fn consider_builtin_generator_candidate(
215+
ecx: &mut EvalCtxt<'_, 'tcx>,
216+
goal: Goal<'tcx, Self>,
217+
) -> QueryResult<'tcx> {
218+
let self_ty = goal.predicate.self_ty();
219+
let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
220+
return Err(NoSolution);
221+
};
222+
223+
// `async`-desugared generators do not implement the generator trait
224+
let tcx = ecx.tcx();
225+
if tcx.generator_is_async(def_id) {
226+
return Err(NoSolution);
227+
}
228+
229+
let generator = substs.as_generator();
230+
Self::consider_assumption(
231+
ecx,
232+
goal,
233+
ty::Binder::dummy(
234+
tcx.mk_trait_ref(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
235+
)
236+
.to_predicate(tcx),
237+
)
238+
}
195239
}
196240

197241
impl<'tcx> EvalCtxt<'_, 'tcx> {

compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
173173
}
174174
}
175175

176+
// Returns a binder of the tupled inputs types and output type from a builtin callable type.
176177
pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
177178
tcx: TyCtxt<'tcx>,
178179
self_ty: Ty<'tcx>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0271]: expected `[async block@$DIR/async.rs:12:17: 12:25]` to be a future that resolves to `i32`, but it resolves to `()`
2+
--> $DIR/async.rs:12:17
3+
|
4+
LL | needs_async(async {});
5+
| ----------- ^^^^^^^^ expected `i32`, found `()`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
note: required by a bound in `needs_async`
10+
--> $DIR/async.rs:8:31
11+
|
12+
LL | fn needs_async(_: impl Future<Output = i32>) {}
13+
| ^^^^^^^^^^^^ required by this bound in `needs_async`
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0271`.

tests/ui/traits/new-solver/async.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// edition: 2021
3+
// revisions: pass fail
4+
//[pass] check-pass
5+
6+
use std::future::Future;
7+
8+
fn needs_async(_: impl Future<Output = i32>) {}
9+
10+
#[cfg(fail)]
11+
fn main() {
12+
needs_async(async {});
13+
//[fail]~^ ERROR to be a future that resolves to `i32`, but it resolves to `()`
14+
}
15+
16+
#[cfg(pass)]
17+
fn main() {
18+
needs_async(async { 1i32 });
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
error[E0277]: the trait bound `[generator@$DIR/generator.rs:18:21: 18:23]: Generator<A>` is not satisfied
2+
--> $DIR/generator.rs:18:21
3+
|
4+
LL | needs_generator(|| {
5+
| _____---------------_^
6+
| | |
7+
| | required by a bound introduced by this call
8+
LL | |
9+
LL | |
10+
LL | |
11+
LL | | yield ();
12+
LL | | });
13+
| |_____^ the trait `Generator<A>` is not implemented for `[generator@$DIR/generator.rs:18:21: 18:23]`
14+
|
15+
note: required by a bound in `needs_generator`
16+
--> $DIR/generator.rs:14:28
17+
|
18+
LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `needs_generator`
20+
21+
error[E0271]: type mismatch resolving `<[generator@$DIR/generator.rs:18:21: 18:23] as Generator<A>>::Yield == B`
22+
--> $DIR/generator.rs:18:21
23+
|
24+
LL | needs_generator(|| {
25+
| _____---------------_^
26+
| | |
27+
| | required by a bound introduced by this call
28+
LL | |
29+
LL | |
30+
LL | |
31+
LL | | yield ();
32+
LL | | });
33+
| |_____^ types differ
34+
|
35+
note: required by a bound in `needs_generator`
36+
--> $DIR/generator.rs:14:41
37+
|
38+
LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
39+
| ^^^^^^^^^ required by this bound in `needs_generator`
40+
41+
error[E0271]: type mismatch resolving `<[generator@$DIR/generator.rs:18:21: 18:23] as Generator<A>>::Return == C`
42+
--> $DIR/generator.rs:18:21
43+
|
44+
LL | needs_generator(|| {
45+
| _____---------------_^
46+
| | |
47+
| | required by a bound introduced by this call
48+
LL | |
49+
LL | |
50+
LL | |
51+
LL | | yield ();
52+
LL | | });
53+
| |_____^ types differ
54+
|
55+
note: required by a bound in `needs_generator`
56+
--> $DIR/generator.rs:14:52
57+
|
58+
LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
59+
| ^^^^^^^^^^ required by this bound in `needs_generator`
60+
61+
error: aborting due to 3 previous errors
62+
63+
Some errors have detailed explanations: E0271, E0277.
64+
For more information about an error, try `rustc --explain E0271`.
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// edition: 2021
3+
// revisions: pass fail
4+
//[pass] check-pass
5+
6+
#![feature(generator_trait, generators)]
7+
8+
use std::ops::Generator;
9+
10+
struct A;
11+
struct B;
12+
struct C;
13+
14+
fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
15+
16+
#[cfg(fail)]
17+
fn main() {
18+
needs_generator(|| {
19+
//[fail]~^ ERROR Generator<A>` is not satisfied
20+
//[fail]~| ERROR as Generator<A>>::Yield == B`
21+
//[fail]~| ERROR as Generator<A>>::Return == C`
22+
yield ();
23+
});
24+
}
25+
26+
#[cfg(pass)]
27+
fn main() {
28+
needs_generator(|_: A| {
29+
let _: A = yield B;
30+
C
31+
})
32+
}

0 commit comments

Comments
 (0)