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:
bors 2021-07-04 17:39:37 +00:00
commit 23c652dfe3
13 changed files with 168 additions and 58 deletions

View file

@ -46,13 +46,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
{ {
self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
Canonicalizer::canonicalize( Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state)
value,
Some(self),
self.tcx,
&CanonicalizeAllFreeRegions,
query_state,
)
} }
/// Canonicalizes a query *response* `V`. When we canonicalize a /// 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(); let mut query_state = OriginalQueryValues::default();
Canonicalizer::canonicalize( Canonicalizer::canonicalize(
value, value,
Some(self), self,
self.tcx, self.tcx,
&CanonicalizeQueryResponse, &CanonicalizeQueryResponse,
&mut query_state, &mut query_state,
@ -101,7 +95,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
let mut query_state = OriginalQueryValues::default(); let mut query_state = OriginalQueryValues::default();
Canonicalizer::canonicalize( Canonicalizer::canonicalize(
value, value,
Some(self), self,
self.tcx, self.tcx,
&CanonicalizeUserTypeAnnotation, &CanonicalizeUserTypeAnnotation,
&mut query_state, &mut query_state,
@ -133,7 +127,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
Canonicalizer::canonicalize( Canonicalizer::canonicalize(
value, value,
Some(self), self,
self.tcx, self.tcx,
&CanonicalizeFreeRegionsOtherThanStatic, &CanonicalizeFreeRegionsOtherThanStatic,
query_state, query_state,
@ -275,7 +269,7 @@ impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
} }
struct Canonicalizer<'cx, 'tcx> { struct Canonicalizer<'cx, 'tcx> {
infcx: Option<&'cx InferCtxt<'cx, 'tcx>>, infcx: &'cx InferCtxt<'cx, 'tcx>,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>, variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>,
query_state: &'cx mut OriginalQueryValues<'tcx>, query_state: &'cx mut OriginalQueryValues<'tcx>,
@ -316,7 +310,6 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
ty::ReVar(vid) => { ty::ReVar(vid) => {
let resolved_vid = self let resolved_vid = self
.infcx .infcx
.unwrap()
.inner .inner
.borrow_mut() .borrow_mut()
.unwrap_region_constraints() .unwrap_region_constraints()
@ -343,7 +336,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
match *t.kind() { match *t.kind() {
ty::Infer(ty::TyVar(vid)) => { ty::Infer(ty::TyVar(vid)) => {
debug!("canonical: type var found with vid {:?}", 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. // `t` could be a float / int variable; canonicalize that instead.
Ok(t) => { Ok(t) => {
debug!("(resolved to {:?})", t); debug!("(resolved to {:?})", t);
@ -429,7 +422,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
match ct.val { match ct.val {
ty::ConstKind::Infer(InferConst::Var(vid)) => { ty::ConstKind::Infer(InferConst::Var(vid)) => {
debug!("canonical: const var found with vid {:?}", 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) => { Ok(c) => {
debug!("(resolved to {:?})", c); debug!("(resolved to {:?})", c);
return self.fold_const(c); return self.fold_const(c);
@ -476,7 +469,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
/// `canonicalize_query` and `canonicalize_response`. /// `canonicalize_query` and `canonicalize_response`.
fn canonicalize<V>( fn canonicalize<V>(
value: V, value: V,
infcx: Option<&InferCtxt<'_, 'tcx>>, infcx: &InferCtxt<'_, 'tcx>,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
canonicalize_region_mode: &dyn CanonicalizeRegionMode, canonicalize_region_mode: &dyn CanonicalizeRegionMode,
query_state: &mut OriginalQueryValues<'tcx>, query_state: &mut OriginalQueryValues<'tcx>,
@ -610,7 +603,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
/// Returns the universe in which `vid` is defined. /// Returns the universe in which `vid` is defined.
fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex { 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`) /// 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 /// *that*. Otherwise, create a new canonical variable for
/// `ty_var`. /// `ty_var`.
fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> { 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); let bound_to = infcx.shallow_resolve(ty_var);
if bound_to != ty_var { if bound_to != ty_var {
self.fold_ty(bound_to) self.fold_ty(bound_to)
@ -650,7 +643,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
info: CanonicalVarInfo<'tcx>, info: CanonicalVarInfo<'tcx>,
const_var: &'tcx ty::Const<'tcx>, const_var: &'tcx ty::Const<'tcx>,
) -> &'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); let bound_to = infcx.shallow_resolve(const_var);
if bound_to != const_var { if bound_to != const_var {
self.fold_const(bound_to) self.fold_const(bound_to)

View file

@ -1559,9 +1559,22 @@ rustc_queries! {
desc { "evaluating trait selection obligation `{}`", goal.value } 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( query type_implements_trait(
key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, ) key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, )
) -> bool { ) -> traits::EvaluationResult {
desc { "evaluating `type_implements_trait` `{:?}`", key } desc { "evaluating `type_implements_trait` `{:?}`", key }
} }

View file

@ -88,23 +88,32 @@ struct NormalizeAfterErasingRegionsFolder<'tcx> {
param_env: ty::ParamEnv<'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> { impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx self.tcx
} }
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
let arg = self.param_env.and(ty.into()); self.normalize_generic_arg_after_erasing_regions(ty.into()).expect_ty()
self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_ty()
} }
fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
let arg = self.param_env.and(c.into()); self.normalize_generic_arg_after_erasing_regions(c.into()).expect_const()
self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_const()
} }
#[inline] #[inline]
fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> { 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); let arg = self.param_env.and(c);
self.tcx.normalize_mir_const_after_erasing_regions(arg) self.tcx.normalize_mir_const_after_erasing_regions(arg)
} }

View file

@ -816,6 +816,15 @@ impl<'tcx> ty::TyS<'tcx> {
[component_ty] => component_ty, [component_ty] => component_ty,
_ => self, _ => 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 // This doesn't depend on regions, so try to minimize distinct
// query keys used. // query keys used.
let erased = tcx.normalize_erasing_regions(param_env, query_ty); let erased = tcx.normalize_erasing_regions(param_env, query_ty);

View file

@ -9,7 +9,7 @@ use rustc_middle::mir::{
FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, 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::source_map::DesugaringKind;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
@ -1329,9 +1329,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let return_ty = tcx.erase_regions(return_ty); let return_ty = tcx.erase_regions(return_ty);
// to avoid panics // to avoid panics
if !return_ty.has_infer_types() {
if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) { 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 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) { if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
err.span_suggestion_hidden( err.span_suggestion_hidden(
@ -1344,7 +1345,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
} }
} }
} }
}
Some(err) Some(err)
} }

View file

@ -2396,7 +2396,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
normalized_ty, normalized_ty,
); );
debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation); 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 let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
if snippet.ends_with('?') { if snippet.ends_with('?') {
err.span_suggestion_verbose( err.span_suggestion_verbose(

View file

@ -542,8 +542,7 @@ fn vtable_trait_first_method_offset<'tcx>(
} }
/// Check whether a `ty` implements given trait(trait_def_id). /// Check whether a `ty` implements given trait(trait_def_id).
/// /// See query definition for details.
/// NOTE: Always return `false` for a type which needs inference.
fn type_implements_trait<'tcx>( fn type_implements_trait<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
key: ( key: (
@ -552,7 +551,7 @@ fn type_implements_trait<'tcx>(
SubstsRef<'tcx>, SubstsRef<'tcx>,
ParamEnv<'tcx>, ParamEnv<'tcx>,
), ),
) -> bool { ) -> EvaluationResult {
let (trait_def_id, ty, params, param_env) = key; let (trait_def_id, ty, params, param_env) = key;
debug!( 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) }; 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 { let obligation = Obligation {
cause: ObligationCause::dummy(), cause: ObligationCause::dummy(),
param_env, param_env,
recursion_depth: 0, recursion_depth: 0,
predicate: trait_ref.without_const().to_predicate(tcx), 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) { pub fn provide(providers: &mut ty::query::Providers) {

View file

@ -440,16 +440,10 @@ impl<'a, 'tcx> CastCheck<'tcx> {
let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
let expr_ty = fcx.tcx.erase_regions(expr_ty); let expr_ty = fcx.tcx.erase_regions(expr_ty);
let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]); let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]);
// Check for infer types because cases like `Option<{integer}>` would if fcx
// panic otherwise. .tcx
if !expr_ty.has_infer_types() .type_implements_trait((from_trait, ty, ty_params, fcx.param_env))
&& !ty.has_infer_types() .must_apply_modulo_regions()
&& fcx.tcx.type_implements_trait((
from_trait,
ty,
ty_params,
fcx.param_env,
))
{ {
label = false; label = false;
err.span_suggestion( err.span_suggestion(

View file

@ -961,12 +961,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let is_drop_defined_for_ty = |ty: Ty<'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 drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, Some(closure_span));
let ty_params = self.tcx.mk_substs_trait(base_path_ty, &[]); let ty_params = self.tcx.mk_substs_trait(base_path_ty, &[]);
self.tcx.type_implements_trait(( self.tcx
.type_implements_trait((
drop_trait, drop_trait,
ty, ty,
ty_params, ty_params,
self.tcx.param_env(closure_def_id.expect_local()), 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); let is_drop_defined_for_ty = is_drop_defined_for_ty(base_path_ty);

View 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>() {
}

View 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`.

View file

@ -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() { }

View file

@ -128,7 +128,9 @@ pub fn implements_trait<'tcx>(
return false; return false;
} }
let ty_params = cx.tcx.mk_substs(ty_params.iter()); 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`. /// Checks whether this type implements `Drop`.