Skip to content

Commit e60abaf

Browse files
committed
-Znext-solver: slightly strenghten closure sig inference
1 parent 9951a99 commit e60abaf

14 files changed

+276
-115
lines changed

compiler/rustc_hir_typeck/src/closure.rs

Lines changed: 74 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,18 @@ use rustc_hir as hir;
99
use rustc_hir::lang_items::LangItem;
1010
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
1111
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, InferResult};
12-
use rustc_infer::traits::{ObligationCauseCode, PredicateObligations};
12+
use rustc_infer::traits::{ObligationCause, ObligationCauseCode, PredicateObligations};
1313
use rustc_macros::{TypeFoldable, TypeVisitable};
1414
use rustc_middle::span_bug;
15+
use rustc_middle::ty::error::TypeError;
1516
use rustc_middle::ty::{
16-
self, ClosureKind, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
17-
TypeVisitableExt, TypeVisitor,
17+
self, ClosureKind, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
18+
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
1819
};
1920
use rustc_span::def_id::LocalDefId;
2021
use rustc_span::{DUMMY_SP, Span};
2122
use rustc_trait_selection::error_reporting::traits::ArgKind;
22-
use rustc_trait_selection::traits;
23+
use rustc_trait_selection::traits::{self, ObligationCtxt};
2324
use tracing::{debug, instrument, trace};
2425

2526
use super::{CoroutineTypes, Expectation, FnCtxt, check_fn};
@@ -384,56 +385,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
384385
// Make sure that we didn't infer a signature that mentions itself.
385386
// This can happen when we elaborate certain supertrait bounds that
386387
// mention projections containing the `Self` type. See #105401.
387-
struct MentionsTy<'tcx> {
388-
expected_ty: Ty<'tcx>,
389-
}
390-
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MentionsTy<'tcx> {
391-
type Result = ControlFlow<()>;
392-
393-
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
394-
if t == self.expected_ty {
395-
ControlFlow::Break(())
396-
} else {
397-
t.super_visit_with(self)
398-
}
399-
}
400-
}
401-
402-
// Don't infer a closure signature from a goal that names the closure type as this will
403-
// (almost always) lead to occurs check errors later in type checking.
388+
//
389+
// Doing so will (almost always) lead to occurs check errors later in
390+
// type checking.
404391
if self.next_trait_solver()
405392
&& let Some(inferred_sig) = inferred_sig
406393
{
407-
// In the new solver it is difficult to explicitly normalize the inferred signature as we
408-
// would have to manually handle universes and rewriting bound vars and placeholders back
409-
// and forth.
410-
//
411-
// Instead we take advantage of the fact that we relating an inference variable with an alias
412-
// will only instantiate the variable if the alias is rigid(*not quite). Concretely we:
413-
// - Create some new variable `?sig`
414-
// - Equate `?sig` with the unnormalized signature, e.g. `fn(<Foo<?x> as Trait>::Assoc)`
415-
// - Depending on whether `<Foo<?x> as Trait>::Assoc` is rigid, ambiguous or normalizeable,
416-
// we will either wind up with `?sig=<Foo<?x> as Trait>::Assoc/?y/ConcreteTy` respectively.
417-
//
418-
// *: In cases where there are ambiguous aliases in the signature that make use of bound vars
419-
// they will wind up present in `?sig` even though they are non-rigid.
394+
// If we've got `F: FnOnce(<u32 as Id<F>>::This)` we want to
395+
// use this to infer the signature `FnOnce(u32)` for the closure.
420396
//
421-
// This is a bit weird and means we may wind up discarding the goal due to it naming `expected_ty`
422-
// even though the normalized form may not name `expected_ty`. However, this matches the existing
423-
// behaviour of the old solver and would be technically a breaking change to fix.
397+
// We handle self-referential aliases here by relying on generalization
398+
// which replaces such aliases with inference variables. This is currently
399+
// a bit too weak, see trait-system-refactor-initiative#191.
400+
struct ReplaceTy<'tcx> {
401+
tcx: TyCtxt<'tcx>,
402+
expected_ty: Ty<'tcx>,
403+
with_ty: Ty<'tcx>,
404+
}
405+
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceTy<'tcx> {
406+
fn cx(&self) -> TyCtxt<'tcx> {
407+
self.tcx
408+
}
409+
410+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
411+
if t == self.expected_ty {
412+
self.with_ty
413+
} else {
414+
t.super_fold_with(self)
415+
}
416+
}
417+
}
424418
let generalized_fnptr_sig = self.next_ty_var(span);
425419
let inferred_fnptr_sig = Ty::new_fn_ptr(self.tcx, inferred_sig.sig);
426-
self.demand_eqtype(span, inferred_fnptr_sig, generalized_fnptr_sig);
427-
428-
let resolved_sig = self.resolve_vars_if_possible(generalized_fnptr_sig);
429-
430-
if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() {
431-
expected_sig = Some(ExpectedSig {
432-
cause_span: inferred_sig.cause_span,
433-
sig: resolved_sig.fn_sig(self.tcx),
434-
});
420+
let inferred_fnptr_sig = inferred_fnptr_sig.fold_with(&mut ReplaceTy {
421+
tcx: self.tcx,
422+
expected_ty,
423+
with_ty: generalized_fnptr_sig,
424+
});
425+
let resolved_sig = self.commit_if_ok(|snapshot| {
426+
let outer_universe = self.universe();
427+
let ocx = ObligationCtxt::new(self);
428+
ocx.eq(
429+
&ObligationCause::dummy(),
430+
self.param_env,
431+
generalized_fnptr_sig,
432+
inferred_fnptr_sig,
433+
)?;
434+
if ocx.select_where_possible().is_empty() {
435+
self.leak_check(outer_universe, Some(snapshot))?;
436+
Ok(self.resolve_vars_if_possible(generalized_fnptr_sig))
437+
} else {
438+
Err(TypeError::Mismatch)
439+
}
440+
});
441+
match resolved_sig {
442+
Ok(resolved_sig) => {
443+
expected_sig = Some(ExpectedSig {
444+
cause_span: inferred_sig.cause_span,
445+
sig: resolved_sig.fn_sig(self.tcx),
446+
})
447+
}
448+
Err(_) => {}
435449
}
436450
} else {
451+
struct MentionsTy<'tcx> {
452+
expected_ty: Ty<'tcx>,
453+
}
454+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MentionsTy<'tcx> {
455+
type Result = ControlFlow<()>;
456+
457+
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
458+
if t == self.expected_ty {
459+
ControlFlow::Break(())
460+
} else {
461+
t.super_visit_with(self)
462+
}
463+
}
464+
}
437465
if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() {
438466
expected_sig = inferred_sig;
439467
}

compiler/rustc_infer/src/infer/relate/generalize.rs

Lines changed: 3 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use rustc_hir::def_id::DefId;
66
use rustc_middle::bug;
77
use rustc_middle::ty::error::TypeError;
88
use rustc_middle::ty::{
9-
self, AliasRelationDirection, InferConst, Term, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
10-
TypeVisitableExt, TypeVisitor, TypingMode,
9+
self, AliasRelationDirection, InferConst, Term, Ty, TyCtxt, TypeVisitableExt, TypingMode,
1110
};
1211
use rustc_span::Span;
1312
use tracing::{debug, instrument, warn};
@@ -290,45 +289,6 @@ impl<'tcx> InferCtxt<'tcx> {
290289
}
291290
}
292291

293-
/// Finds the max universe present
294-
struct MaxUniverse {
295-
max_universe: ty::UniverseIndex,
296-
}
297-
298-
impl MaxUniverse {
299-
fn new() -> Self {
300-
MaxUniverse { max_universe: ty::UniverseIndex::ROOT }
301-
}
302-
303-
fn max_universe(self) -> ty::UniverseIndex {
304-
self.max_universe
305-
}
306-
}
307-
308-
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxUniverse {
309-
fn visit_ty(&mut self, t: Ty<'tcx>) {
310-
if let ty::Placeholder(placeholder) = t.kind() {
311-
self.max_universe = self.max_universe.max(placeholder.universe);
312-
}
313-
314-
t.super_visit_with(self)
315-
}
316-
317-
fn visit_const(&mut self, c: ty::Const<'tcx>) {
318-
if let ty::ConstKind::Placeholder(placeholder) = c.kind() {
319-
self.max_universe = self.max_universe.max(placeholder.universe);
320-
}
321-
322-
c.super_visit_with(self)
323-
}
324-
325-
fn visit_region(&mut self, r: ty::Region<'tcx>) {
326-
if let ty::RePlaceholder(placeholder) = r.kind() {
327-
self.max_universe = self.max_universe.max(placeholder.universe);
328-
}
329-
}
330-
}
331-
332292
/// The "generalizer" is used when handling inference variables.
333293
///
334294
/// The basic strategy for handling a constraint like `?A <: B` is to
@@ -437,15 +397,8 @@ impl<'tcx> Generalizer<'_, 'tcx> {
437397
if is_nested_alias {
438398
return Err(e);
439399
} else {
440-
let mut visitor = MaxUniverse::new();
441-
alias.visit_with(&mut visitor);
442-
let infer_replacement_is_complete =
443-
self.for_universe.can_name(visitor.max_universe())
444-
&& !alias.has_escaping_bound_vars();
445-
if !infer_replacement_is_complete {
446-
warn!("may incompletely handle alias type: {alias:?}");
447-
}
448-
400+
// FIXME(trait-system-refactor-initiative#8): This is incomplete
401+
// in case the alias has escaping bound vars.
449402
debug!("generalization failure in alias");
450403
Ok(self.next_ty_var_for_alias())
451404
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//@ revisions: current next
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
3+
//@[next] compile-flags: -Znext-solver
4+
//@ check-pass
5+
6+
// Regression test for an issue found in #146720.
7+
8+
trait Trait {
9+
type Assoc;
10+
}
11+
12+
struct W<T>(T);
13+
impl<T: Trait> Trait for W<T>
14+
where
15+
u32: Trait<Assoc = T::Assoc>,
16+
{
17+
type Assoc = T;
18+
}
19+
20+
impl<T> Trait for (T,) {
21+
type Assoc = T;
22+
}
23+
24+
impl Trait for u32 {
25+
type Assoc = u32;
26+
}
27+
28+
fn foo<T: Trait>(_: impl FnOnce(T::Assoc)) {}
29+
30+
fn main() {
31+
// The closure signature is `fn(<W<?infer> as Trait>::Assoc)`.
32+
// Normalizing it results in `?t` with `u32: Trait<Assoc = <?t>::Assoc>`.
33+
// Equating `?t` with the argument pattern constrains it to `(?t,)`, at
34+
// which point the `u32: Trait<Assoc = <?t>::Assoc>` obligations constrains
35+
// `(?t,)` to `(u32,)`.
36+
//
37+
// This breaks when fudging inference to replace `?t` with an unconstrained
38+
// infer var.
39+
foo::<W<_>>(|(field,)| { let _ = field.count_ones(); })
40+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
error: implementation of `Foo` is not general enough
2+
--> $DIR/higher-ranked-alias-norm-to-hr.rs:23:5
3+
|
4+
LL | needs_super(|_| {});
5+
| ^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
6+
|
7+
= note: `{closure@$DIR/higher-ranked-alias-norm-to-hr.rs:23:17: 23:20}` must implement `Foo<'0>`, for any lifetime `'0`...
8+
= note: ...but it actually implements `Foo<'1>`, for some specific lifetime `'1`
9+
10+
error: implementation of `Fn` is not general enough
11+
--> $DIR/higher-ranked-alias-norm-to-hr.rs:23:5
12+
|
13+
LL | needs_super(|_| {});
14+
| ^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
15+
|
16+
= note: closure with signature `fn(&'2 u32)` must implement `Fn<(&'1 u32,)>`, for any lifetime `'1`...
17+
= note: ...but it actually implements `Fn<(&'2 u32,)>`, for some specific lifetime `'2`
18+
19+
error: implementation of `Foo` is not general enough
20+
--> $DIR/higher-ranked-alias-norm-to-hr.rs:23:5
21+
|
22+
LL | needs_super(|_| {});
23+
| ^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
24+
|
25+
= note: `{closure@$DIR/higher-ranked-alias-norm-to-hr.rs:23:17: 23:20}` must implement `Foo<'0>`, for any lifetime `'0`...
26+
= note: ...but it actually implements `Foo<'1>`, for some specific lifetime `'1`
27+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
28+
29+
error: implementation of `FnOnce` is not general enough
30+
--> $DIR/higher-ranked-alias-norm-to-hr.rs:23:5
31+
|
32+
LL | needs_super(|_| {});
33+
| ^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
34+
|
35+
= note: closure with signature `fn(&'2 u32)` must implement `FnOnce<(&'1 u32,)>`, for any lifetime `'1`...
36+
= note: ...but it actually implements `FnOnce<(&'2 u32,)>`, for some specific lifetime `'2`
37+
38+
error: implementation of `Foo` is not general enough
39+
--> $DIR/higher-ranked-alias-norm-to-hr.rs:23:5
40+
|
41+
LL | needs_super(|_| {});
42+
| ^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
43+
|
44+
= note: `{closure@$DIR/higher-ranked-alias-norm-to-hr.rs:23:17: 23:20}` must implement `Foo<'0>`, for any lifetime `'0`...
45+
= note: ...but it actually implements `Foo<'1>`, for some specific lifetime `'1`
46+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
47+
48+
error: aborting due to 5 previous errors
49+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
error[E0277]: expected a `Fn(&'a u32)` closure, found `{closure@$DIR/higher-ranked-alias-norm-to-hr.rs:23:17: 23:20}`
2+
--> $DIR/higher-ranked-alias-norm-to-hr.rs:23:17
3+
|
4+
LL | needs_super(|_| {});
5+
| ----------- ^^^^^^ expected an `Fn(&'a u32)` closure, found `{closure@$DIR/higher-ranked-alias-norm-to-hr.rs:23:17: 23:20}`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the trait `for<'a> Fn(&'a u32)` is not implemented for closure `{closure@$DIR/higher-ranked-alias-norm-to-hr.rs:23:17: 23:20}`
10+
= note: expected a closure with signature `for<'a> fn(&'a _)`
11+
found a closure with signature `fn(&_)`
12+
note: this is a known limitation of the trait solver that will be lifted in the future
13+
--> $DIR/higher-ranked-alias-norm-to-hr.rs:23:17
14+
|
15+
LL | needs_super(|_| {});
16+
| ------------^^^----
17+
| | |
18+
| | the trait solver is unable to infer the generic types that should be inferred from this argument
19+
| add turbofish arguments to this call to specify the types manually, even if it's redundant
20+
note: required by a bound in `needs_super`
21+
--> $DIR/higher-ranked-alias-norm-to-hr.rs:17:19
22+
|
23+
LL | fn needs_super<F: for<'a> Fn(<F as Foo<'a>>::Input) + for<'a> Foo<'a>>(_: F) {}
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `needs_super`
25+
26+
error[E0277]: the trait bound `for<'a> {closure@$DIR/higher-ranked-alias-norm-to-hr.rs:23:17: 23:20}: Foo<'a>` is not satisfied
27+
--> $DIR/higher-ranked-alias-norm-to-hr.rs:23:17
28+
|
29+
LL | needs_super(|_| {});
30+
| ----------- ^^^^^^ unsatisfied trait bound
31+
| |
32+
| required by a bound introduced by this call
33+
|
34+
= help: the trait `for<'a> Foo<'a>` is not implemented for closure `{closure@$DIR/higher-ranked-alias-norm-to-hr.rs:23:17: 23:20}`
35+
note: this is a known limitation of the trait solver that will be lifted in the future
36+
--> $DIR/higher-ranked-alias-norm-to-hr.rs:23:17
37+
|
38+
LL | needs_super(|_| {});
39+
| ------------^^^----
40+
| | |
41+
| | the trait solver is unable to infer the generic types that should be inferred from this argument
42+
| add turbofish arguments to this call to specify the types manually, even if it's redundant
43+
note: required by a bound in `needs_super`
44+
--> $DIR/higher-ranked-alias-norm-to-hr.rs:17:55
45+
|
46+
LL | fn needs_super<F: for<'a> Fn(<F as Foo<'a>>::Input) + for<'a> Foo<'a>>(_: F) {}
47+
| ^^^^^^^^^^^^^^^ required by this bound in `needs_super`
48+
49+
error: aborting due to 2 previous errors
50+
51+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ revisions: current_ok next_ok current_ambig next_ambig
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
3+
//@[next_ok] compile-flags: -Znext-solver
4+
//@[next_ambig] compile-flags: -Znext-solver
5+
//@[current_ok] check-pass
6+
//@[next_ok] check-pass
7+
8+
// Regression test for trait-system-refactor-initiative#191.
9+
trait Foo<'a> {
10+
type Input;
11+
}
12+
13+
impl<'a, F: Fn(&'a u32)> Foo<'a> for F {
14+
type Input = &'a u32;
15+
}
16+
17+
fn needs_super<F: for<'a> Fn(<F as Foo<'a>>::Input) + for<'a> Foo<'a>>(_: F) {}
18+
19+
fn main() {
20+
#[cfg(any(current_ok, next_ok))]
21+
needs_super(|_: &u32| {});
22+
#[cfg(any(current_ambig, next_ambig))]
23+
needs_super(|_| {});
24+
//[next_ambig]~^ ERROR expected a `Fn(&'a u32)` closure, found
25+
//[next_ambig]~| ERROR the trait bound
26+
//[current_ambig]~^^^ ERROR implementation of `Foo` is not general enough
27+
//[current_ambig]~| ERROR implementation of `Fn` is not general enough
28+
//[current_ambig]~| ERROR implementation of `Foo` is not general enough
29+
//[current_ambig]~| ERROR implementation of `FnOnce` is not general enough
30+
//[current_ambig]~| ERROR implementation of `Foo` is not general enough
31+
}

0 commit comments

Comments
 (0)