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`
This commit is contained in:
commit
23c652dfe3
13 changed files with 168 additions and 58 deletions
|
@ -46,13 +46,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
|||
{
|
||||
self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
Canonicalizer::canonicalize(
|
||||
value,
|
||||
Some(self),
|
||||
self.tcx,
|
||||
&CanonicalizeAllFreeRegions,
|
||||
query_state,
|
||||
)
|
||||
Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state)
|
||||
}
|
||||
|
||||
/// Canonicalizes a query *response* `V`. When we canonicalize a
|
||||
|
@ -87,7 +81,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
|||
let mut query_state = OriginalQueryValues::default();
|
||||
Canonicalizer::canonicalize(
|
||||
value,
|
||||
Some(self),
|
||||
self,
|
||||
self.tcx,
|
||||
&CanonicalizeQueryResponse,
|
||||
&mut query_state,
|
||||
|
@ -101,7 +95,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
|||
let mut query_state = OriginalQueryValues::default();
|
||||
Canonicalizer::canonicalize(
|
||||
value,
|
||||
Some(self),
|
||||
self,
|
||||
self.tcx,
|
||||
&CanonicalizeUserTypeAnnotation,
|
||||
&mut query_state,
|
||||
|
@ -133,7 +127,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
|||
|
||||
Canonicalizer::canonicalize(
|
||||
value,
|
||||
Some(self),
|
||||
self,
|
||||
self.tcx,
|
||||
&CanonicalizeFreeRegionsOtherThanStatic,
|
||||
query_state,
|
||||
|
@ -275,7 +269,7 @@ impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
|
|||
}
|
||||
|
||||
struct Canonicalizer<'cx, 'tcx> {
|
||||
infcx: Option<&'cx InferCtxt<'cx, 'tcx>>,
|
||||
infcx: &'cx InferCtxt<'cx, 'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>,
|
||||
query_state: &'cx mut OriginalQueryValues<'tcx>,
|
||||
|
@ -316,7 +310,6 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
|
|||
ty::ReVar(vid) => {
|
||||
let resolved_vid = self
|
||||
.infcx
|
||||
.unwrap()
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.unwrap_region_constraints()
|
||||
|
@ -343,7 +336,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
|
|||
match *t.kind() {
|
||||
ty::Infer(ty::TyVar(vid)) => {
|
||||
debug!("canonical: type var found with vid {:?}", vid);
|
||||
match self.infcx.unwrap().probe_ty_var(vid) {
|
||||
match self.infcx.probe_ty_var(vid) {
|
||||
// `t` could be a float / int variable; canonicalize that instead.
|
||||
Ok(t) => {
|
||||
debug!("(resolved to {:?})", t);
|
||||
|
@ -429,7 +422,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
|
|||
match ct.val {
|
||||
ty::ConstKind::Infer(InferConst::Var(vid)) => {
|
||||
debug!("canonical: const var found with vid {:?}", vid);
|
||||
match self.infcx.unwrap().probe_const_var(vid) {
|
||||
match self.infcx.probe_const_var(vid) {
|
||||
Ok(c) => {
|
||||
debug!("(resolved to {:?})", c);
|
||||
return self.fold_const(c);
|
||||
|
@ -476,7 +469,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
|
|||
/// `canonicalize_query` and `canonicalize_response`.
|
||||
fn canonicalize<V>(
|
||||
value: V,
|
||||
infcx: Option<&InferCtxt<'_, 'tcx>>,
|
||||
infcx: &InferCtxt<'_, 'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
canonicalize_region_mode: &dyn CanonicalizeRegionMode,
|
||||
query_state: &mut OriginalQueryValues<'tcx>,
|
||||
|
@ -610,7 +603,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
|
|||
|
||||
/// Returns the universe in which `vid` is defined.
|
||||
fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
|
||||
self.infcx.unwrap().inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
|
||||
self.infcx.inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
|
||||
}
|
||||
|
||||
/// Creates a canonical variable (with the given `info`)
|
||||
|
@ -631,7 +624,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
|
|||
/// *that*. Otherwise, create a new canonical variable for
|
||||
/// `ty_var`.
|
||||
fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let infcx = self.infcx.expect("encountered ty-var without infcx");
|
||||
let infcx = self.infcx;
|
||||
let bound_to = infcx.shallow_resolve(ty_var);
|
||||
if bound_to != ty_var {
|
||||
self.fold_ty(bound_to)
|
||||
|
@ -650,7 +643,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
|
|||
info: CanonicalVarInfo<'tcx>,
|
||||
const_var: &'tcx ty::Const<'tcx>,
|
||||
) -> &'tcx ty::Const<'tcx> {
|
||||
let infcx = self.infcx.expect("encountered const-var without infcx");
|
||||
let infcx = self.infcx;
|
||||
let bound_to = infcx.shallow_resolve(const_var);
|
||||
if bound_to != const_var {
|
||||
self.fold_const(bound_to)
|
||||
|
|
|
@ -1559,9 +1559,22 @@ rustc_queries! {
|
|||
desc { "evaluating trait selection obligation `{}`", goal.value }
|
||||
}
|
||||
|
||||
/// Evaluates whether the given type implements the given trait
|
||||
/// in the given environment.
|
||||
///
|
||||
/// The inputs are:
|
||||
///
|
||||
/// - the def-id of the trait
|
||||
/// - the self type
|
||||
/// - the *other* type parameters of the trait, excluding the self-type
|
||||
/// - the parameter environment
|
||||
///
|
||||
/// FIXME. If the type, trait, or environment has inference variables,
|
||||
/// this yields `EvaluatedToUnknown`. It should be refactored
|
||||
/// to use canonicalization, really.
|
||||
query type_implements_trait(
|
||||
key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, )
|
||||
) -> bool {
|
||||
) -> traits::EvaluationResult {
|
||||
desc { "evaluating `type_implements_trait` `{:?}`", key }
|
||||
}
|
||||
|
||||
|
|
|
@ -88,23 +88,32 @@ struct NormalizeAfterErasingRegionsFolder<'tcx> {
|
|||
param_env: ty::ParamEnv<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> {
|
||||
fn normalize_generic_arg_after_erasing_regions(
|
||||
&self,
|
||||
arg: ty::GenericArg<'tcx>,
|
||||
) -> ty::GenericArg<'tcx> {
|
||||
let arg = self.param_env.and(arg);
|
||||
self.tcx.normalize_generic_arg_after_erasing_regions(arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let arg = self.param_env.and(ty.into());
|
||||
self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_ty()
|
||||
self.normalize_generic_arg_after_erasing_regions(ty.into()).expect_ty()
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
|
||||
let arg = self.param_env.and(c.into());
|
||||
self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_const()
|
||||
self.normalize_generic_arg_after_erasing_regions(c.into()).expect_const()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
|
||||
// FIXME: This *probably* needs canonicalization too!
|
||||
let arg = self.param_env.and(c);
|
||||
self.tcx.normalize_mir_const_after_erasing_regions(arg)
|
||||
}
|
||||
|
|
|
@ -816,6 +816,15 @@ impl<'tcx> ty::TyS<'tcx> {
|
|||
[component_ty] => component_ty,
|
||||
_ => self,
|
||||
};
|
||||
|
||||
// FIXME(#86868): We should be canonicalizing, or else moving this to a method of inference
|
||||
// context, or *something* like that, but for now just avoid passing inference
|
||||
// variables to queries that can't cope with them. Instead, conservatively
|
||||
// return "true" (may change drop order).
|
||||
if query_ty.needs_infer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// This doesn't depend on regions, so try to minimize distinct
|
||||
// query keys used.
|
||||
let erased = tcx.normalize_erasing_regions(param_env, query_ty);
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc_middle::mir::{
|
|||
FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
|
||||
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
|
||||
};
|
||||
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TypeFoldable};
|
||||
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
|
||||
use rustc_span::source_map::DesugaringKind;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
@ -1329,18 +1329,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
let return_ty = tcx.erase_regions(return_ty);
|
||||
|
||||
// to avoid panics
|
||||
if !return_ty.has_infer_types() {
|
||||
if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
|
||||
if tcx.type_implements_trait((iter_trait, return_ty, ty_params, self.param_env))
|
||||
{
|
||||
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
|
||||
err.span_suggestion_hidden(
|
||||
return_span,
|
||||
"use `.collect()` to allocate the iterator",
|
||||
format!("{}{}", snippet, ".collect::<Vec<_>>()"),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
|
||||
if tcx
|
||||
.type_implements_trait((iter_trait, return_ty, ty_params, self.param_env))
|
||||
.must_apply_modulo_regions()
|
||||
{
|
||||
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
|
||||
err.span_suggestion_hidden(
|
||||
return_span,
|
||||
"use `.collect()` to allocate the iterator",
|
||||
format!("{}{}", snippet, ".collect::<Vec<_>>()"),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2396,7 +2396,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
normalized_ty,
|
||||
);
|
||||
debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation);
|
||||
if self.predicate_may_hold(&try_obligation) && impls_future {
|
||||
if self.predicate_may_hold(&try_obligation)
|
||||
&& impls_future.must_apply_modulo_regions()
|
||||
{
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
if snippet.ends_with('?') {
|
||||
err.span_suggestion_verbose(
|
||||
|
|
|
@ -542,8 +542,7 @@ fn vtable_trait_first_method_offset<'tcx>(
|
|||
}
|
||||
|
||||
/// Check whether a `ty` implements given trait(trait_def_id).
|
||||
///
|
||||
/// NOTE: Always return `false` for a type which needs inference.
|
||||
/// See query definition for details.
|
||||
fn type_implements_trait<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
key: (
|
||||
|
@ -552,7 +551,7 @@ fn type_implements_trait<'tcx>(
|
|||
SubstsRef<'tcx>,
|
||||
ParamEnv<'tcx>,
|
||||
),
|
||||
) -> bool {
|
||||
) -> EvaluationResult {
|
||||
let (trait_def_id, ty, params, param_env) = key;
|
||||
|
||||
debug!(
|
||||
|
@ -562,13 +561,22 @@ fn type_implements_trait<'tcx>(
|
|||
|
||||
let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, params) };
|
||||
|
||||
// FIXME(#86868): If there are inference variables anywhere, just give up and assume
|
||||
// we don't know the answer. This works around the ICEs that would result from
|
||||
// using those inference variables within the `infer_ctxt` we create below.
|
||||
// Really we should be using canonicalized variables, or perhaps removing
|
||||
// this query altogether.
|
||||
if (trait_ref, param_env).needs_infer() {
|
||||
return EvaluationResult::EvaluatedToUnknown;
|
||||
}
|
||||
|
||||
let obligation = Obligation {
|
||||
cause: ObligationCause::dummy(),
|
||||
param_env,
|
||||
recursion_depth: 0,
|
||||
predicate: trait_ref.without_const().to_predicate(tcx),
|
||||
};
|
||||
tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
|
||||
tcx.infer_ctxt().enter(|infcx| infcx.evaluate_obligation_no_overflow(&obligation))
|
||||
}
|
||||
|
||||
pub fn provide(providers: &mut ty::query::Providers) {
|
||||
|
|
|
@ -440,16 +440,10 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
|
||||
let expr_ty = fcx.tcx.erase_regions(expr_ty);
|
||||
let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]);
|
||||
// Check for infer types because cases like `Option<{integer}>` would
|
||||
// panic otherwise.
|
||||
if !expr_ty.has_infer_types()
|
||||
&& !ty.has_infer_types()
|
||||
&& fcx.tcx.type_implements_trait((
|
||||
from_trait,
|
||||
ty,
|
||||
ty_params,
|
||||
fcx.param_env,
|
||||
))
|
||||
if fcx
|
||||
.tcx
|
||||
.type_implements_trait((from_trait, ty, ty_params, fcx.param_env))
|
||||
.must_apply_modulo_regions()
|
||||
{
|
||||
label = false;
|
||||
err.span_suggestion(
|
||||
|
|
|
@ -961,12 +961,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let is_drop_defined_for_ty = |ty: Ty<'tcx>| {
|
||||
let drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, Some(closure_span));
|
||||
let ty_params = self.tcx.mk_substs_trait(base_path_ty, &[]);
|
||||
self.tcx.type_implements_trait((
|
||||
drop_trait,
|
||||
ty,
|
||||
ty_params,
|
||||
self.tcx.param_env(closure_def_id.expect_local()),
|
||||
))
|
||||
self.tcx
|
||||
.type_implements_trait((
|
||||
drop_trait,
|
||||
ty,
|
||||
ty_params,
|
||||
self.tcx.param_env(closure_def_id.expect_local()),
|
||||
))
|
||||
.must_apply_modulo_regions()
|
||||
};
|
||||
|
||||
let is_drop_defined_for_ty = is_drop_defined_for_ty(base_path_ty);
|
||||
|
|
16
src/test/ui/async-await/issue-84841.rs
Normal file
16
src/test/ui/async-await/issue-84841.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
// edition:2018
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
|
||||
async fn foo() {
|
||||
// Adding an .await here avoids the ICE
|
||||
test()?;
|
||||
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||
//~| ERROR the `?` operator can only be used in an async function that returns
|
||||
}
|
||||
|
||||
// Removing the const generic parameter here avoids the ICE
|
||||
async fn test<const N: usize>() {
|
||||
}
|
28
src/test/ui/async-await/issue-84841.stderr
Normal file
28
src/test/ui/async-await/issue-84841.stderr
Normal file
|
@ -0,0 +1,28 @@
|
|||
error[E0277]: the `?` operator can only be applied to values that implement `Try`
|
||||
--> $DIR/issue-84841.rs:9:5
|
||||
|
|
||||
LL | test()?;
|
||||
| ^^^^^^^ the `?` operator cannot be applied to type `impl Future`
|
||||
|
|
||||
= help: the trait `Try` is not implemented for `impl Future`
|
||||
= note: required by `branch`
|
||||
|
||||
error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `FromResidual`)
|
||||
--> $DIR/issue-84841.rs:9:11
|
||||
|
|
||||
LL | async fn foo() {
|
||||
| ________________-
|
||||
LL | | // Adding an .await here avoids the ICE
|
||||
LL | | test()?;
|
||||
| | ^ cannot use the `?` operator in an async function that returns `()`
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_- this function should return `Result` or `Option` to accept `?`
|
||||
|
|
||||
= help: the trait `FromResidual<_>` is not implemented for `()`
|
||||
= note: required by `from_residual`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
|
@ -0,0 +1,34 @@
|
|||
// edition:2018
|
||||
// check-pass
|
||||
|
||||
#![warn(rust_2021_compatibility)]
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
struct Runtime;
|
||||
|
||||
impl Runtime {
|
||||
pub fn block_on<F: Future>(&self, _future: F) -> F::Output {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn http<F, Fut>(_func: F)
|
||||
where
|
||||
F: Fn() -> Fut,
|
||||
Fut: Future<Output = ()>,
|
||||
{
|
||||
let rt = Runtime {};
|
||||
let srv = rt.block_on(async move { serve(move || async move { unimplemented!() }) });
|
||||
let _ = || rt.block_on(async { srv });
|
||||
}
|
||||
|
||||
pub struct Server<S> {
|
||||
_marker: std::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
pub fn serve<S>(_new_service: S) -> Server<S> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn main() { }
|
|
@ -128,7 +128,9 @@ pub fn implements_trait<'tcx>(
|
|||
return false;
|
||||
}
|
||||
let ty_params = cx.tcx.mk_substs(ty_params.iter());
|
||||
cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
|
||||
cx.tcx
|
||||
.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
|
||||
.must_apply_modulo_regions()
|
||||
}
|
||||
|
||||
/// Checks whether this type implements `Drop`.
|
||||
|
|
Loading…
Add table
Reference in a new issue