diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index c68705da413f8..ac953f4305c31 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -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( 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) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 23ee0e0506272..0986a46572969 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -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 } } diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index a4f736654af44..11399506b96e4 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -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) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 7bf69b9e637e9..0a158423dc487 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -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); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index a0c9b43d5afee..4be4372c0393a 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -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::>()"), - 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::>()"), + Applicability::MaybeIncorrect, + ); } } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 5c35b515f3d02..dc765f5228b9d 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -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( diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index d65a596a8276f..e48aab6f46f4b 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -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) { diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 3cbc3d231f847..a09fad3b6b78a 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -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( diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 41b4172781cf8..e5f18778f43e6 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -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); diff --git a/src/test/ui/async-await/issue-84841.rs b/src/test/ui/async-await/issue-84841.rs new file mode 100644 index 0000000000000..ba3a1617b9c1d --- /dev/null +++ b/src/test/ui/async-await/issue-84841.rs @@ -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() { +} diff --git a/src/test/ui/async-await/issue-84841.stderr b/src/test/ui/async-await/issue-84841.stderr new file mode 100644 index 0000000000000..170dcf581ed26 --- /dev/null +++ b/src/test/ui/async-await/issue-84841.stderr @@ -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`. diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs b/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs new file mode 100644 index 0000000000000..fce9cac627b5e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs @@ -0,0 +1,34 @@ +// edition:2018 +// check-pass + +#![warn(rust_2021_compatibility)] + +use std::future::Future; + +struct Runtime; + +impl Runtime { + pub fn block_on(&self, _future: F) -> F::Output { + unimplemented!() + } +} + +pub fn http(_func: F) +where + F: Fn() -> Fut, + Fut: Future, +{ + let rt = Runtime {}; + let srv = rt.block_on(async move { serve(move || async move { unimplemented!() }) }); + let _ = || rt.block_on(async { srv }); +} + +pub struct Server { + _marker: std::marker::PhantomData, +} + +pub fn serve(_new_service: S) -> Server { + unimplemented!() +} + +fn main() { } diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index a92d3be5d3cf2..2808fc35e2a1b 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -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`.