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_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index a0c9b43d5afee..c1c875eac5589 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -1331,7 +1331,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // 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 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( 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..c5c1da2d2c028 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: 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..7ff4a108dabcf 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -444,12 +444,15 @@ impl<'a, 'tcx> CastCheck<'tcx> { // 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, - )) + && 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 index 2336778832301..ba3a1617b9c1d 100644 --- a/src/test/ui/async-await/issue-84841.rs +++ b/src/test/ui/async-await/issue-84841.rs @@ -1,8 +1,14 @@ // edition:2018 -async fn main() { +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 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/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index a92d3be5d3cf2..add487593c6d2 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`. @@ -144,22 +146,26 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind() { ty::Adt(adt, _) => must_use_attr(cx.tcx.get_attrs(adt.did)).is_some(), ty::Foreign(ref did) => must_use_attr(cx.tcx.get_attrs(*did)).is_some(), - ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { + ty::Slice(ty) + | ty::Array(ty, _) + | ty::RawPtr(ty::TypeAndMut { ty, .. }) + | ty::Ref(_, ty, _) => { // for the Array case we don't need to care for the len == 0 case // because we don't want to lint functions returning empty arrays is_must_use_ty(cx, *ty) - }, + } ty::Tuple(substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), ty::Opaque(ref def_id, _) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { - if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() { + if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() + { if must_use_attr(cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { return true; } } } false - }, + } ty::Dynamic(binder, _) => { for predicate in binder.iter() { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { @@ -169,7 +175,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } false - }, + } _ => false, } } @@ -179,7 +185,11 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // not succeed /// Checks if `Ty` is normalizable. This function is useful /// to avoid crashes on `layout_of`. -pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { +pub fn is_normalizable<'tcx>( + cx: &LateContext<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, +) -> bool { is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default()) } @@ -199,15 +209,14 @@ fn is_normalizable_helper<'tcx>( if infcx.at(&cause, param_env).normalize(ty).is_ok() { match ty.kind() { ty::Adt(def, substs) => def.variants.iter().all(|variant| { - variant - .fields - .iter() - .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) + variant.fields.iter().all(|field| { + is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache) + }) }), _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { GenericArgKind::Type(inner_ty) if inner_ty != ty => { is_normalizable_helper(cx, param_env, inner_ty, cache) - }, + } _ => true, // if inner_ty == ty, we've already checked it }), } @@ -225,7 +234,9 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { match ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true, - ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type), + ty::Array(inner_type, _) | ty::Slice(inner_type) => { + is_recursively_primitive_type(inner_type) + } ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type), _ => false, } @@ -269,11 +280,7 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { /// removed. pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { - if let ty::Ref(_, ty, _) = ty.kind() { - peel(ty, count + 1) - } else { - (ty, count) - } + if let ty::Ref(_, ty, _) = ty.kind() { peel(ty, count + 1) } else { (ty, count) } } peel(ty, 0) } @@ -328,17 +335,18 @@ pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { return false; } - substs_a - .iter() - .zip(substs_b.iter()) - .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) { - (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, + substs_a.iter().zip(substs_b.iter()).all(|(arg_a, arg_b)| { + match (arg_a.unpack(), arg_b.unpack()) { + (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => { + inner_a == inner_b + } (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { same_type_and_consts(type_a, type_b) - }, + } _ => true, - }) - }, + } + }) + } _ => a == b, } }