Skip to content

Commit 23c652d

Browse files
committed
Auto merge of #86866 - nikomatsakis:issue-84841, r=oli-obk
Hack: Ignore inference variables in certain queries Fixes #84841 Fixes #86753 Some queries are not built to accept types with inference variables, which can lead to ICEs. These queries probably ought to be converted to canonical form, but as a quick workaround, we can return conservative results in the case that inference variables are found. We should file a follow-up issue (and update the FIXMEs...) to do the proper refactoring. cc `@arora-aman` r? `@oli-obk`
2 parents 9044245 + 492ba34 commit 23c652d

File tree

13 files changed

+168
-58
lines changed

13 files changed

+168
-58
lines changed

compiler/rustc_infer/src/infer/canonical/canonicalizer.rs

+11-18
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
4646
{
4747
self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
4848

49-
Canonicalizer::canonicalize(
50-
value,
51-
Some(self),
52-
self.tcx,
53-
&CanonicalizeAllFreeRegions,
54-
query_state,
55-
)
49+
Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state)
5650
}
5751

5852
/// Canonicalizes a query *response* `V`. When we canonicalize a
@@ -87,7 +81,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
8781
let mut query_state = OriginalQueryValues::default();
8882
Canonicalizer::canonicalize(
8983
value,
90-
Some(self),
84+
self,
9185
self.tcx,
9286
&CanonicalizeQueryResponse,
9387
&mut query_state,
@@ -101,7 +95,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
10195
let mut query_state = OriginalQueryValues::default();
10296
Canonicalizer::canonicalize(
10397
value,
104-
Some(self),
98+
self,
10599
self.tcx,
106100
&CanonicalizeUserTypeAnnotation,
107101
&mut query_state,
@@ -133,7 +127,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
133127

134128
Canonicalizer::canonicalize(
135129
value,
136-
Some(self),
130+
self,
137131
self.tcx,
138132
&CanonicalizeFreeRegionsOtherThanStatic,
139133
query_state,
@@ -275,7 +269,7 @@ impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
275269
}
276270

277271
struct Canonicalizer<'cx, 'tcx> {
278-
infcx: Option<&'cx InferCtxt<'cx, 'tcx>>,
272+
infcx: &'cx InferCtxt<'cx, 'tcx>,
279273
tcx: TyCtxt<'tcx>,
280274
variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>,
281275
query_state: &'cx mut OriginalQueryValues<'tcx>,
@@ -316,7 +310,6 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
316310
ty::ReVar(vid) => {
317311
let resolved_vid = self
318312
.infcx
319-
.unwrap()
320313
.inner
321314
.borrow_mut()
322315
.unwrap_region_constraints()
@@ -343,7 +336,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
343336
match *t.kind() {
344337
ty::Infer(ty::TyVar(vid)) => {
345338
debug!("canonical: type var found with vid {:?}", vid);
346-
match self.infcx.unwrap().probe_ty_var(vid) {
339+
match self.infcx.probe_ty_var(vid) {
347340
// `t` could be a float / int variable; canonicalize that instead.
348341
Ok(t) => {
349342
debug!("(resolved to {:?})", t);
@@ -429,7 +422,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
429422
match ct.val {
430423
ty::ConstKind::Infer(InferConst::Var(vid)) => {
431424
debug!("canonical: const var found with vid {:?}", vid);
432-
match self.infcx.unwrap().probe_const_var(vid) {
425+
match self.infcx.probe_const_var(vid) {
433426
Ok(c) => {
434427
debug!("(resolved to {:?})", c);
435428
return self.fold_const(c);
@@ -476,7 +469,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
476469
/// `canonicalize_query` and `canonicalize_response`.
477470
fn canonicalize<V>(
478471
value: V,
479-
infcx: Option<&InferCtxt<'_, 'tcx>>,
472+
infcx: &InferCtxt<'_, 'tcx>,
480473
tcx: TyCtxt<'tcx>,
481474
canonicalize_region_mode: &dyn CanonicalizeRegionMode,
482475
query_state: &mut OriginalQueryValues<'tcx>,
@@ -610,7 +603,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
610603

611604
/// Returns the universe in which `vid` is defined.
612605
fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
613-
self.infcx.unwrap().inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
606+
self.infcx.inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
614607
}
615608

616609
/// Creates a canonical variable (with the given `info`)
@@ -631,7 +624,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
631624
/// *that*. Otherwise, create a new canonical variable for
632625
/// `ty_var`.
633626
fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> {
634-
let infcx = self.infcx.expect("encountered ty-var without infcx");
627+
let infcx = self.infcx;
635628
let bound_to = infcx.shallow_resolve(ty_var);
636629
if bound_to != ty_var {
637630
self.fold_ty(bound_to)
@@ -650,7 +643,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
650643
info: CanonicalVarInfo<'tcx>,
651644
const_var: &'tcx ty::Const<'tcx>,
652645
) -> &'tcx ty::Const<'tcx> {
653-
let infcx = self.infcx.expect("encountered const-var without infcx");
646+
let infcx = self.infcx;
654647
let bound_to = infcx.shallow_resolve(const_var);
655648
if bound_to != const_var {
656649
self.fold_const(bound_to)

compiler/rustc_middle/src/query/mod.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -1559,9 +1559,22 @@ rustc_queries! {
15591559
desc { "evaluating trait selection obligation `{}`", goal.value }
15601560
}
15611561

1562+
/// Evaluates whether the given type implements the given trait
1563+
/// in the given environment.
1564+
///
1565+
/// The inputs are:
1566+
///
1567+
/// - the def-id of the trait
1568+
/// - the self type
1569+
/// - the *other* type parameters of the trait, excluding the self-type
1570+
/// - the parameter environment
1571+
///
1572+
/// FIXME. If the type, trait, or environment has inference variables,
1573+
/// this yields `EvaluatedToUnknown`. It should be refactored
1574+
/// to use canonicalization, really.
15621575
query type_implements_trait(
15631576
key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, )
1564-
) -> bool {
1577+
) -> traits::EvaluationResult {
15651578
desc { "evaluating `type_implements_trait` `{:?}`", key }
15661579
}
15671580

compiler/rustc_middle/src/ty/normalize_erasing_regions.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -88,23 +88,32 @@ struct NormalizeAfterErasingRegionsFolder<'tcx> {
8888
param_env: ty::ParamEnv<'tcx>,
8989
}
9090

91+
impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> {
92+
fn normalize_generic_arg_after_erasing_regions(
93+
&self,
94+
arg: ty::GenericArg<'tcx>,
95+
) -> ty::GenericArg<'tcx> {
96+
let arg = self.param_env.and(arg);
97+
self.tcx.normalize_generic_arg_after_erasing_regions(arg)
98+
}
99+
}
100+
91101
impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> {
92102
fn tcx(&self) -> TyCtxt<'tcx> {
93103
self.tcx
94104
}
95105

96106
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
97-
let arg = self.param_env.and(ty.into());
98-
self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_ty()
107+
self.normalize_generic_arg_after_erasing_regions(ty.into()).expect_ty()
99108
}
100109

101110
fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
102-
let arg = self.param_env.and(c.into());
103-
self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_const()
111+
self.normalize_generic_arg_after_erasing_regions(c.into()).expect_const()
104112
}
105113

106114
#[inline]
107115
fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
116+
// FIXME: This *probably* needs canonicalization too!
108117
let arg = self.param_env.and(c);
109118
self.tcx.normalize_mir_const_after_erasing_regions(arg)
110119
}

compiler/rustc_middle/src/ty/util.rs

+9
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,15 @@ impl<'tcx> ty::TyS<'tcx> {
816816
[component_ty] => component_ty,
817817
_ => self,
818818
};
819+
820+
// FIXME(#86868): We should be canonicalizing, or else moving this to a method of inference
821+
// context, or *something* like that, but for now just avoid passing inference
822+
// variables to queries that can't cope with them. Instead, conservatively
823+
// return "true" (may change drop order).
824+
if query_ty.needs_infer() {
825+
return true;
826+
}
827+
819828
// This doesn't depend on regions, so try to minimize distinct
820829
// query keys used.
821830
let erased = tcx.normalize_erasing_regions(param_env, query_ty);

compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_middle::mir::{
99
FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
1010
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
1111
};
12-
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TypeFoldable};
12+
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
1313
use rustc_span::source_map::DesugaringKind;
1414
use rustc_span::symbol::sym;
1515
use rustc_span::{Span, DUMMY_SP};
@@ -1329,18 +1329,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
13291329
let return_ty = tcx.erase_regions(return_ty);
13301330

13311331
// to avoid panics
1332-
if !return_ty.has_infer_types() {
1333-
if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1334-
if tcx.type_implements_trait((iter_trait, return_ty, ty_params, self.param_env))
1335-
{
1336-
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1337-
err.span_suggestion_hidden(
1338-
return_span,
1339-
"use `.collect()` to allocate the iterator",
1340-
format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1341-
Applicability::MaybeIncorrect,
1342-
);
1343-
}
1332+
if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1333+
if tcx
1334+
.type_implements_trait((iter_trait, return_ty, ty_params, self.param_env))
1335+
.must_apply_modulo_regions()
1336+
{
1337+
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1338+
err.span_suggestion_hidden(
1339+
return_span,
1340+
"use `.collect()` to allocate the iterator",
1341+
format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1342+
Applicability::MaybeIncorrect,
1343+
);
13441344
}
13451345
}
13461346
}

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2396,7 +2396,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
23962396
normalized_ty,
23972397
);
23982398
debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation);
2399-
if self.predicate_may_hold(&try_obligation) && impls_future {
2399+
if self.predicate_may_hold(&try_obligation)
2400+
&& impls_future.must_apply_modulo_regions()
2401+
{
24002402
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
24012403
if snippet.ends_with('?') {
24022404
err.span_suggestion_verbose(

compiler/rustc_trait_selection/src/traits/mod.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -542,8 +542,7 @@ fn vtable_trait_first_method_offset<'tcx>(
542542
}
543543

544544
/// Check whether a `ty` implements given trait(trait_def_id).
545-
///
546-
/// NOTE: Always return `false` for a type which needs inference.
545+
/// See query definition for details.
547546
fn type_implements_trait<'tcx>(
548547
tcx: TyCtxt<'tcx>,
549548
key: (
@@ -552,7 +551,7 @@ fn type_implements_trait<'tcx>(
552551
SubstsRef<'tcx>,
553552
ParamEnv<'tcx>,
554553
),
555-
) -> bool {
554+
) -> EvaluationResult {
556555
let (trait_def_id, ty, params, param_env) = key;
557556

558557
debug!(
@@ -562,13 +561,22 @@ fn type_implements_trait<'tcx>(
562561

563562
let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, params) };
564563

564+
// FIXME(#86868): If there are inference variables anywhere, just give up and assume
565+
// we don't know the answer. This works around the ICEs that would result from
566+
// using those inference variables within the `infer_ctxt` we create below.
567+
// Really we should be using canonicalized variables, or perhaps removing
568+
// this query altogether.
569+
if (trait_ref, param_env).needs_infer() {
570+
return EvaluationResult::EvaluatedToUnknown;
571+
}
572+
565573
let obligation = Obligation {
566574
cause: ObligationCause::dummy(),
567575
param_env,
568576
recursion_depth: 0,
569577
predicate: trait_ref.without_const().to_predicate(tcx),
570578
};
571-
tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
579+
tcx.infer_ctxt().enter(|infcx| infcx.evaluate_obligation_no_overflow(&obligation))
572580
}
573581

574582
pub fn provide(providers: &mut ty::query::Providers) {

compiler/rustc_typeck/src/check/cast.rs

+4-10
Original file line numberDiff line numberDiff line change
@@ -440,16 +440,10 @@ impl<'a, 'tcx> CastCheck<'tcx> {
440440
let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
441441
let expr_ty = fcx.tcx.erase_regions(expr_ty);
442442
let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]);
443-
// Check for infer types because cases like `Option<{integer}>` would
444-
// panic otherwise.
445-
if !expr_ty.has_infer_types()
446-
&& !ty.has_infer_types()
447-
&& fcx.tcx.type_implements_trait((
448-
from_trait,
449-
ty,
450-
ty_params,
451-
fcx.param_env,
452-
))
443+
if fcx
444+
.tcx
445+
.type_implements_trait((from_trait, ty, ty_params, fcx.param_env))
446+
.must_apply_modulo_regions()
453447
{
454448
label = false;
455449
err.span_suggestion(

compiler/rustc_typeck/src/check/upvar.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -961,12 +961,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
961961
let is_drop_defined_for_ty = |ty: Ty<'tcx>| {
962962
let drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, Some(closure_span));
963963
let ty_params = self.tcx.mk_substs_trait(base_path_ty, &[]);
964-
self.tcx.type_implements_trait((
965-
drop_trait,
966-
ty,
967-
ty_params,
968-
self.tcx.param_env(closure_def_id.expect_local()),
969-
))
964+
self.tcx
965+
.type_implements_trait((
966+
drop_trait,
967+
ty,
968+
ty_params,
969+
self.tcx.param_env(closure_def_id.expect_local()),
970+
))
971+
.must_apply_modulo_regions()
970972
};
971973

972974
let is_drop_defined_for_ty = is_drop_defined_for_ty(base_path_ty);
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// edition:2018
2+
3+
fn main() {
4+
5+
}
6+
7+
async fn foo() {
8+
// Adding an .await here avoids the ICE
9+
test()?;
10+
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
11+
//~| ERROR the `?` operator can only be used in an async function that returns
12+
}
13+
14+
// Removing the const generic parameter here avoids the ICE
15+
async fn test<const N: usize>() {
16+
}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error[E0277]: the `?` operator can only be applied to values that implement `Try`
2+
--> $DIR/issue-84841.rs:9:5
3+
|
4+
LL | test()?;
5+
| ^^^^^^^ the `?` operator cannot be applied to type `impl Future`
6+
|
7+
= help: the trait `Try` is not implemented for `impl Future`
8+
= note: required by `branch`
9+
10+
error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `FromResidual`)
11+
--> $DIR/issue-84841.rs:9:11
12+
|
13+
LL | async fn foo() {
14+
| ________________-
15+
LL | | // Adding an .await here avoids the ICE
16+
LL | | test()?;
17+
| | ^ cannot use the `?` operator in an async function that returns `()`
18+
LL | |
19+
LL | |
20+
LL | | }
21+
| |_- this function should return `Result` or `Option` to accept `?`
22+
|
23+
= help: the trait `FromResidual<_>` is not implemented for `()`
24+
= note: required by `from_residual`
25+
26+
error: aborting due to 2 previous errors
27+
28+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)