Skip to content

Commit 8a66e39

Browse files
Rollup merge of rust-lang#107061 - compiler-errors:new-solver-new-candidates-3, r=lcnr
Implement some more new solver candidates and fix some bugs First, fix some bugs: 1. `IndexVec::drain_enumerated(a..b)` does not give us an iterator of index keys + items enumerated from `a..b`, but from `0..(b-a)`... That caused a bug. See first commit for the fix. 2. Implement the `_: Trait` ambiguity hack. I put it in assemble, let me know if it should live elsewhere. This is important, since we otherwise consider `_: Sized` to have no solutions, and nothing passes! 3. Swap `Ambiguity` and `Unimplemented` cases for the new solver. Sorry for accidentally swapping them 😄 4. Check GATs' own predicates during projection confirmation. Then implement a few builtin traits: 5. Implement `PointerSized`. Pretty independent. 6. Implement `Fn` family of traits for fnptr, fndef, and closures. Closures are currently broken because `FulfillCtxt::relationships` is intentionally left unimplemented. See comment in the test. r? `@lcnr`
2 parents 4b7bec8 + ed6aebb commit 8a66e39

File tree

10 files changed

+272
-21
lines changed

10 files changed

+272
-21
lines changed

compiler/rustc_index/src/vec.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,12 @@ impl<I: Idx, T> IndexVec<I, T> {
207207
&'a mut self,
208208
range: R,
209209
) -> impl Iterator<Item = (I, T)> + 'a {
210-
self.raw.drain(range).enumerate().map(|(n, t)| (I::new(n), t))
210+
let begin = match range.start_bound() {
211+
std::ops::Bound::Included(i) => *i,
212+
std::ops::Bound::Excluded(i) => i.checked_add(1).unwrap(),
213+
std::ops::Bound::Unbounded => 0,
214+
};
215+
self.raw.drain(range).enumerate().map(move |(n, t)| (I::new(begin + n), t))
211216
}
212217

213218
#[inline]

compiler/rustc_trait_selection/src/solve/assembly.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Code shared by trait and projection goals for candidate assembly.
22
33
use super::infcx_ext::InferCtxtExt;
4-
use super::{CanonicalResponse, EvalCtxt, Goal, QueryResult};
4+
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
55
use rustc_hir::def_id::DefId;
66
use rustc_infer::traits::query::NoSolution;
77
use rustc_infer::traits::util::elaborate_predicates;
@@ -79,7 +79,7 @@ pub(super) enum CandidateSource {
7979
AliasBound(usize),
8080
}
8181

82-
pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy {
82+
pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
8383
fn self_ty(self) -> Ty<'tcx>;
8484

8585
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
@@ -117,13 +117,43 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy {
117117
ecx: &mut EvalCtxt<'_, 'tcx>,
118118
goal: Goal<'tcx, Self>,
119119
) -> QueryResult<'tcx>;
120+
121+
fn consider_builtin_pointer_sized_candidate(
122+
ecx: &mut EvalCtxt<'_, 'tcx>,
123+
goal: Goal<'tcx, Self>,
124+
) -> QueryResult<'tcx>;
125+
126+
fn consider_builtin_fn_trait_candidates(
127+
ecx: &mut EvalCtxt<'_, 'tcx>,
128+
goal: Goal<'tcx, Self>,
129+
kind: ty::ClosureKind,
130+
) -> QueryResult<'tcx>;
131+
132+
fn consider_builtin_tuple_candidate(
133+
ecx: &mut EvalCtxt<'_, 'tcx>,
134+
goal: Goal<'tcx, Self>,
135+
) -> QueryResult<'tcx>;
120136
}
121137

122138
impl<'tcx> EvalCtxt<'_, 'tcx> {
123139
pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<'tcx>>(
124140
&mut self,
125141
goal: Goal<'tcx, G>,
126142
) -> Vec<Candidate<'tcx>> {
143+
debug_assert_eq!(goal, self.infcx.resolve_vars_if_possible(goal));
144+
145+
// HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule,
146+
// object bound, alias bound, etc. We are unable to determine this until we can at
147+
// least structually resolve the type one layer.
148+
if goal.predicate.self_ty().is_ty_var() {
149+
return vec![Candidate {
150+
source: CandidateSource::BuiltinImpl,
151+
result: self
152+
.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity))
153+
.unwrap(),
154+
}];
155+
}
156+
127157
let mut candidates = Vec::new();
128158

129159
self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates);
@@ -169,6 +199,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
169199
Ok((_, certainty)) => certainty,
170200
Err(NoSolution) => return,
171201
};
202+
let normalized_ty = self.infcx.resolve_vars_if_possible(normalized_ty);
172203

173204
// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
174205
// This doesn't work as long as we use `CandidateSource` in winnowing.
@@ -224,6 +255,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
224255
|| lang_items.clone_trait() == Some(trait_def_id)
225256
{
226257
G::consider_builtin_copy_clone_candidate(self, goal)
258+
} else if lang_items.pointer_sized() == Some(trait_def_id) {
259+
G::consider_builtin_pointer_sized_candidate(self, goal)
260+
} else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) {
261+
G::consider_builtin_fn_trait_candidates(self, goal, kind)
262+
} else if lang_items.tuple_trait() == Some(trait_def_id) {
263+
G::consider_builtin_tuple_candidate(self, goal)
227264
} else {
228265
Err(NoSolution)
229266
};

compiler/rustc_trait_selection/src/solve/fulfill.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
5252
.drain(..)
5353
.map(|obligation| FulfillmentError {
5454
obligation: obligation.clone(),
55-
code: FulfillmentErrorCode::CodeSelectionError(SelectionError::Unimplemented),
55+
code: FulfillmentErrorCode::CodeAmbiguity,
5656
root_obligation: obligation,
5757
})
5858
.collect()
@@ -75,7 +75,9 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
7575
Err(NoSolution) => {
7676
errors.push(FulfillmentError {
7777
obligation: obligation.clone(),
78-
code: FulfillmentErrorCode::CodeAmbiguity,
78+
code: FulfillmentErrorCode::CodeSelectionError(
79+
SelectionError::Unimplemented,
80+
),
7981
root_obligation: obligation,
8082
});
8183
continue;

compiler/rustc_trait_selection/src/solve/project_goals.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::traits::{specialization_graph, translate_substs};
22

33
use super::assembly::{self, Candidate, CandidateSource};
44
use super::infcx_ext::InferCtxtExt;
5+
use super::trait_goals::structural_traits;
56
use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
67
use rustc_errors::ErrorGuaranteed;
78
use rustc_hir::def::DefKind;
@@ -11,9 +12,9 @@ use rustc_infer::traits::query::NoSolution;
1112
use rustc_infer::traits::specialization_graph::LeafDef;
1213
use rustc_infer::traits::Reveal;
1314
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
14-
use rustc_middle::ty::TypeVisitable;
1515
use rustc_middle::ty::{self, Ty, TyCtxt};
1616
use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
17+
use rustc_middle::ty::{ToPredicate, TypeVisitable};
1718
use rustc_span::DUMMY_SP;
1819
use std::iter;
1920
use std::ops::ControlFlow;
@@ -351,6 +352,46 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
351352
) -> QueryResult<'tcx> {
352353
bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal);
353354
}
355+
356+
fn consider_builtin_pointer_sized_candidate(
357+
_ecx: &mut EvalCtxt<'_, 'tcx>,
358+
goal: Goal<'tcx, Self>,
359+
) -> QueryResult<'tcx> {
360+
bug!("`PointerSized` does not have an associated type: {:?}", goal);
361+
}
362+
363+
fn consider_builtin_fn_trait_candidates(
364+
ecx: &mut EvalCtxt<'_, 'tcx>,
365+
goal: Goal<'tcx, Self>,
366+
goal_kind: ty::ClosureKind,
367+
) -> QueryResult<'tcx> {
368+
if let Some(tupled_inputs_and_output) =
369+
structural_traits::extract_tupled_inputs_and_output_from_callable(
370+
ecx.tcx(),
371+
goal.predicate.self_ty(),
372+
goal_kind,
373+
)?
374+
{
375+
let pred = tupled_inputs_and_output
376+
.map_bound(|(inputs, output)| ty::ProjectionPredicate {
377+
projection_ty: ecx
378+
.tcx()
379+
.mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]),
380+
term: output.into(),
381+
})
382+
.to_predicate(ecx.tcx());
383+
Self::consider_assumption(ecx, goal, pred)
384+
} else {
385+
ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity))
386+
}
387+
}
388+
389+
fn consider_builtin_tuple_candidate(
390+
_ecx: &mut EvalCtxt<'_, 'tcx>,
391+
goal: Goal<'tcx, Self>,
392+
) -> QueryResult<'tcx> {
393+
bug!("`Tuple` does not have an associated type: {:?}", goal);
394+
}
354395
}
355396

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

compiler/rustc_trait_selection/src/solve/trait_goals.rs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ use std::iter;
44

55
use super::assembly::{self, Candidate, CandidateSource};
66
use super::infcx_ext::InferCtxtExt;
7-
use super::{EvalCtxt, Goal, QueryResult};
7+
use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
88
use rustc_hir::def_id::DefId;
99
use rustc_infer::infer::InferCtxt;
1010
use rustc_infer::traits::query::NoSolution;
1111
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
12-
use rustc_middle::ty::TraitPredicate;
13-
use rustc_middle::ty::{self, Ty, TyCtxt};
12+
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
13+
use rustc_middle::ty::{TraitPredicate, TypeVisitable};
1414
use rustc_span::DUMMY_SP;
1515

16-
mod structural_traits;
16+
pub mod structural_traits;
1717

1818
impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
1919
fn self_ty(self) -> Ty<'tcx> {
@@ -127,6 +127,64 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
127127
structural_traits::instantiate_constituent_tys_for_copy_clone_trait,
128128
)
129129
}
130+
131+
fn consider_builtin_pointer_sized_candidate(
132+
ecx: &mut EvalCtxt<'_, 'tcx>,
133+
goal: Goal<'tcx, Self>,
134+
) -> QueryResult<'tcx> {
135+
if goal.predicate.self_ty().has_non_region_infer() {
136+
return ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity));
137+
}
138+
139+
let tcx = ecx.tcx();
140+
let self_ty = tcx.erase_regions(goal.predicate.self_ty());
141+
142+
if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty))
143+
&& let usize_layout = tcx.layout_of(ty::ParamEnv::empty().and(tcx.types.usize)).unwrap().layout
144+
&& layout.layout.size() == usize_layout.size()
145+
&& layout.layout.align().abi == usize_layout.align().abi
146+
{
147+
// FIXME: We could make this faster by making a no-constraints response
148+
ecx.make_canonical_response(Certainty::Yes)
149+
} else {
150+
Err(NoSolution)
151+
}
152+
}
153+
154+
fn consider_builtin_fn_trait_candidates(
155+
ecx: &mut EvalCtxt<'_, 'tcx>,
156+
goal: Goal<'tcx, Self>,
157+
goal_kind: ty::ClosureKind,
158+
) -> QueryResult<'tcx> {
159+
if let Some(tupled_inputs_and_output) =
160+
structural_traits::extract_tupled_inputs_and_output_from_callable(
161+
ecx.tcx(),
162+
goal.predicate.self_ty(),
163+
goal_kind,
164+
)?
165+
{
166+
let pred = tupled_inputs_and_output
167+
.map_bound(|(inputs, _)| {
168+
ecx.tcx()
169+
.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
170+
})
171+
.to_predicate(ecx.tcx());
172+
Self::consider_assumption(ecx, goal, pred)
173+
} else {
174+
ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity))
175+
}
176+
}
177+
178+
fn consider_builtin_tuple_candidate(
179+
ecx: &mut EvalCtxt<'_, 'tcx>,
180+
goal: Goal<'tcx, Self>,
181+
) -> QueryResult<'tcx> {
182+
if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
183+
ecx.make_canonical_response(Certainty::Yes)
184+
} else {
185+
Err(NoSolution)
186+
}
187+
}
130188
}
131189

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

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

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_hir::{Movability, Mutability};
22
use rustc_infer::{infer::InferCtxt, traits::query::NoSolution};
3-
use rustc_middle::ty::{self, Ty};
3+
use rustc_middle::ty::{self, Ty, TyCtxt};
44

55
// Calculates the constituent types of a type for `auto trait` purposes.
66
//
@@ -30,10 +30,7 @@ pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
3030
| ty::Foreign(..)
3131
| ty::Alias(ty::Projection, ..)
3232
| ty::Bound(..)
33-
| ty::Infer(ty::TyVar(_)) => {
34-
// FIXME: Do we need to mark anything as ambiguous here? Yeah?
35-
Err(NoSolution)
36-
}
33+
| ty::Infer(ty::TyVar(_)) => Err(NoSolution),
3734

3835
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(),
3936

@@ -101,9 +98,8 @@ pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
10198
| ty::Dynamic(..)
10299
| ty::Foreign(..)
103100
| ty::Alias(..)
104-
| ty::Param(_) => Err(NoSolution),
105-
106-
ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"),
101+
| ty::Param(_)
102+
| ty::Infer(ty::TyVar(_)) => Err(NoSolution),
107103

108104
ty::Placeholder(..)
109105
| ty::Bound(..)
@@ -151,9 +147,8 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
151147
| ty::Ref(_, _, Mutability::Mut)
152148
| ty::Adt(_, _)
153149
| ty::Alias(_, _)
154-
| ty::Param(_) => Err(NoSolution),
155-
156-
ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"),
150+
| ty::Param(_)
151+
| ty::Infer(ty::TyVar(_)) => Err(NoSolution),
157152

158153
ty::Placeholder(..)
159154
| ty::Bound(..)
@@ -177,3 +172,52 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
177172
}
178173
}
179174
}
175+
176+
pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
177+
tcx: TyCtxt<'tcx>,
178+
self_ty: Ty<'tcx>,
179+
goal_kind: ty::ClosureKind,
180+
) -> Result<Option<ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, NoSolution> {
181+
match *self_ty.kind() {
182+
ty::FnDef(def_id, substs) => Ok(Some(
183+
tcx.bound_fn_sig(def_id)
184+
.subst(tcx, substs)
185+
.map_bound(|sig| (tcx.mk_tup(sig.inputs().iter()), sig.output())),
186+
)),
187+
ty::FnPtr(sig) => {
188+
Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs().iter()), sig.output()))))
189+
}
190+
ty::Closure(_, substs) => {
191+
let closure_substs = substs.as_closure();
192+
match closure_substs.kind_ty().to_opt_closure_kind() {
193+
Some(closure_kind) if closure_kind.extends(goal_kind) => {}
194+
None => return Ok(None),
195+
_ => return Err(NoSolution),
196+
}
197+
Ok(Some(closure_substs.sig().map_bound(|sig| (sig.inputs()[0], sig.output()))))
198+
}
199+
ty::Bool
200+
| ty::Char
201+
| ty::Int(_)
202+
| ty::Uint(_)
203+
| ty::Float(_)
204+
| ty::Adt(_, _)
205+
| ty::Foreign(_)
206+
| ty::Str
207+
| ty::Array(_, _)
208+
| ty::Slice(_)
209+
| ty::RawPtr(_)
210+
| ty::Ref(_, _, _)
211+
| ty::Dynamic(_, _, _)
212+
| ty::Generator(_, _, _)
213+
| ty::GeneratorWitness(_)
214+
| ty::Never
215+
| ty::Tuple(_)
216+
| ty::Alias(_, _)
217+
| ty::Param(_)
218+
| ty::Placeholder(_)
219+
| ty::Bound(_, _)
220+
| ty::Infer(_)
221+
| ty::Error(_) => Err(NoSolution),
222+
}
223+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// known-bug: unknown
3+
// failure-status: 101
4+
// dont-check-compiler-stderr
5+
6+
// This test will fail until we fix `FulfillmentCtxt::relationships`. That's
7+
// because we create a type variable for closure upvar types, which is not
8+
// constrained until after we try to do fallback on diverging type variables.
9+
// Thus, we will call that function, which is unimplemented.
10+
11+
fn require_fn(_: impl Fn() -> i32) {}
12+
13+
fn main() {
14+
require_fn(|| -> i32 { 1i32 });
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// check-pass
3+
4+
fn require_fn(_: impl Fn() -> i32) {}
5+
6+
fn f() -> i32 {
7+
1i32
8+
}
9+
10+
fn main() {
11+
require_fn(f);
12+
require_fn(f as fn() -> i32);
13+
}

0 commit comments

Comments
 (0)