Skip to content

Commit 4974d90

Browse files
committed
-Znext-solver: slightly strenghen closure sig inference
1 parent d1ed52b commit 4974d90

File tree

17 files changed

+243
-121
lines changed

17 files changed

+243
-121
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.fudge_inference_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_hir_typeck/src/expr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2015,7 +2015,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20152015

20162016
let adt_ty = self.try_structurally_resolve_type(path_span, adt_ty);
20172017
let adt_ty_hint = expected.only_has_type(self).and_then(|expected| {
2018-
self.fudge_inference_if_ok(|| {
2018+
self.fudge_inference_if_ok(|_| {
20192019
let ocx = ObligationCtxt::new(self);
20202020
ocx.sup(&self.misc(path_span), self.param_env, expected, adt_ty)?;
20212021
if !ocx.select_where_possible().is_empty() {

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
245245
let expected_input_tys: Option<Vec<_>> = expectation
246246
.only_has_type(self)
247247
.and_then(|expected_output| {
248-
self.fudge_inference_if_ok(|| {
248+
self.fudge_inference_if_ok(|_| {
249249
let ocx = ObligationCtxt::new(self);
250250

251251
// Attempt to apply a subtyping relationship between the formal

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
}

compiler/rustc_infer/src/infer/snapshot/fudge.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use tracing::instrument;
99
use ut::UnifyKey;
1010

1111
use super::VariableLengths;
12+
use crate::infer::snapshot::CombinedSnapshot;
1213
use crate::infer::type_variable::TypeVariableOrigin;
1314
use crate::infer::unify_key::{ConstVariableValue, ConstVidKey};
1415
use crate::infer::{ConstVariableOrigin, InferCtxt, RegionVariableOrigin, UnificationTable};
@@ -87,12 +88,12 @@ impl<'tcx> InferCtxt<'tcx> {
8788
#[instrument(skip(self, f), level = "debug")]
8889
pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
8990
where
90-
F: FnOnce() -> Result<T, E>,
91+
F: FnOnce(&CombinedSnapshot<'tcx>) -> Result<T, E>,
9192
T: TypeFoldable<TyCtxt<'tcx>>,
9293
{
9394
let variable_lengths = self.variable_lengths();
94-
let (snapshot_vars, value) = self.probe(|_| {
95-
let value = f()?;
95+
let (snapshot_vars, value) = self.probe(|snapshot| {
96+
let value = f(snapshot)?;
9697
// At this point, `value` could in principle refer
9798
// to inference variables that have been created during
9899
// the snapshot. Once we exit `probe()`, those are

compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ fn find_best_leaf_obligation<'tcx>(
167167
// We should probably fix the visitor to not do so instead, as this also
168168
// means the leaf obligation may be incorrect.
169169
let obligation = infcx
170-
.fudge_inference_if_ok(|| {
170+
.fudge_inference_if_ok(|_| {
171171
infcx
172172
.visit_proof_tree(
173173
obligation.as_goal(),
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`.

0 commit comments

Comments
 (0)