diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 64c808f2597b3..5296efd4cc4f4 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -174,7 +174,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We won't diverge unless the scrutinee or all arms diverge. self.diverges.set(scrut_diverges | all_arms_diverge); - coercion.complete(self) + let cause = ObligationCause::dummy(); + //let coerce_never = self.tcx.expr_guaranteed_to_constitute_read_for_never(expr); + let coerce_never = true; + tracing::debug!("calling complete in check_expr_match"); + let expected = expected.coercion_target_type(self, expr.span); + coercion.complete(self, &cause, expected, coerce_never) } fn explain_never_type_coerced_to_unit( diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 612396858841f..bd5486650b72b 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -134,7 +134,9 @@ pub(super) fn check_fn<'a, 'tcx>( // really expected to fail, since the coercions would have failed // earlier when trying to find a LUB. let coercion = fcx.ret_coercion.take().unwrap().into_inner(); - let mut actual_return_ty = coercion.complete(fcx); + let cause = ObligationCause::dummy(); + tracing::debug!("calling complete in check_fn"); + let mut actual_return_ty = coercion.complete(fcx, &cause, ret_ty, true); debug!("actual_return_ty = {:?}", actual_return_ty); if let ty::Dynamic(..) = declared_ret_ty.kind() { // We have special-cased the case where the function is declared diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 82b7c578a1f25..b3347f3c83899 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -11,11 +11,11 @@ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, InferResult}; use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; use rustc_macros::{TypeFoldable, TypeVisitable}; -use rustc_middle::span_bug; use rustc_middle::ty::{ self, ClosureKind, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; +use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; use rustc_span::{DUMMY_SP, Span}; use rustc_trait_selection::error_reporting::traits::ArgKind; @@ -340,6 +340,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (None, None) } }, + ty::Closure(_, args) => match closure_kind { + hir::ClosureKind::Closure => { + let closure_args = args.as_closure(); + let sig = closure_args.sig(); + tracing::debug!(?closure_args, ?sig); + let sig = sig.map_bound(|sig| { + let inputs = sig.inputs_and_output.first().unwrap(); + let inputs = match inputs.kind() { + ty::Tuple(tys) => tys, + _ => bug!(), + }; + let output = sig.inputs_and_output.last().unwrap(); + let inputs_and_output = self.tcx.mk_type_list( + &inputs.iter().chain([*output]).collect::>(), + ); + ty::FnSig { + abi: sig.abi, + safety: sig.safety, + c_variadic: sig.c_variadic, + inputs_and_output, + } + }); + tracing::debug!(?sig); + let expected_sig = ExpectedSig { cause_span: None, sig }; + let kind = closure_args.kind_ty().to_opt_closure_kind(); + (Some(expected_sig), kind) + } + hir::ClosureKind::Coroutine(_) | hir::ClosureKind::CoroutineClosure(_) => { + (None, None) + } + }, _ => (None, None), } } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 88d2e80f1521e..503b2f8445df5 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -55,7 +55,7 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::{BytePos, DUMMY_SP, Span}; +use rustc_span::{BytePos, DUMMY_SP, ErrorGuaranteed, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor}; use rustc_trait_selection::solve::{Certainty, Goal, NoSolution}; @@ -120,6 +120,7 @@ fn success<'tcx>( /// FIXME: We may want to change type relations to always leak-check /// after exiting a binder, at which point we will always do so and /// no longer need to handle this explicitly +#[derive(Debug)] enum ForceLeakCheck { Yes, No, @@ -192,6 +193,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } /// Unify two types (using sub or lub). + #[tracing::instrument(skip(self), ret)] fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>, leak_check: ForceLeakCheck) -> CoerceResult<'tcx> { self.unify_raw(a, b, leak_check) .and_then(|InferOk { value: ty, obligations }| success(vec![], ty, obligations)) @@ -225,6 +227,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let b = self.shallow_resolve(b); debug!("Coerce.tys({:?} => {:?})", a, b); + // Are you kidding me!? + // I *wanted* to add a short-circuit here to return `success(vec![], a, PredicateObligations::new())` when + // `a == b`, which I *thought* was trivially true and correct: they are the same types, so they obviously don't + // need any adjustments or anything, right!? Wrong! You forgot reborrows! So, this breaks static mut refs, of + // all things. Whomp whomp. Try again another time. -Jack + // Coercing from `!` to any type is allowed: if a.is_never() { if self.coerce_never { @@ -310,6 +318,184 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } + /// This is *almost* like `coerce(new_ty, prev_ty)` followed by `coerce(prev_ty, new_ty)`, but is + /// not quite in case of `!` and inference variables. + #[allow(dead_code)] + #[instrument(skip(self), ret)] + fn coerce_mutual( + &self, + prev_ty: Ty<'tcx>, + new_ty: Ty<'tcx>, + ) -> InferResult<'tcx, (Vec>, Ty<'tcx>, bool)> { + // First, remove any resolved type variables (at the top level, at least): + let prev_ty = self.shallow_resolve(prev_ty); + let new_ty = self.shallow_resolve(new_ty); + debug!("coerce_mutual({:?} => {:?})", prev_ty, new_ty); + + // There is no coercion *from* any type *to* `!`, other than just unification + + // Coercing from `!` to any type is allowed: + if new_ty.is_never() { + let ret = if self.coerce_never { + success( + vec![Adjustment { kind: Adjust::NeverToAny, target: prev_ty }], + prev_ty, + PredicateObligations::new(), + ) + } else { + // Otherwise the only coercion we can do is unification. + self.unify(new_ty, prev_ty, ForceLeakCheck::No) + }; + return ret.map(|r| InferOk { + obligations: r.obligations, + value: (r.value.0, r.value.1, true), + }); + } + + if prev_ty.is_never() { + let ret = if self.coerce_never { + success( + vec![Adjustment { kind: Adjust::NeverToAny, target: new_ty }], + new_ty, + PredicateObligations::new(), + ) + } else { + // Otherwise the only coercion we can do is unification. + self.unify(prev_ty, new_ty, ForceLeakCheck::No) + }; + return ret.map(|r| InferOk { + obligations: r.obligations, + value: (r.value.0, r.value.1, false), + }); + } + + // Coercing *from* an unresolved inference variable means that + // we have no information about the source type. This will always + // ultimately fall back to some form of subtyping. + if new_ty.is_ty_var() { + let ret = self.coerce_from_inference_variable(new_ty, prev_ty); + return ret.map(|r| InferOk { + obligations: r.obligations, + value: (r.value.0, r.value.1, true), + }); + } + + if prev_ty.is_ty_var() { + let ret = self.coerce_from_inference_variable(prev_ty, new_ty); + return ret.map(|r| InferOk { + obligations: r.obligations, + value: (r.value.0, r.value.1, false), + }); + } + + let coerce_inner = |a: Ty<'tcx>, b: Ty<'tcx>, unify_fallback: bool| -> CoerceResult<'tcx> { + let span = tracing::debug_span!("coerce_inner", ?a, ?b); + let _span = span.enter(); + + // Consider coercing the subtype to a DST + // + // NOTE: this is wrapped in a `commit_if_ok` because it creates + // a "spurious" type variable, and we don't want to have that + // type variable in memory if the coercion fails. + let unsize = self.commit_if_ok(|_| self.coerce_unsized(a, b)); + match unsize { + Ok(_) => { + debug!("coerce: unsize successful"); + return unsize; + } + Err(error) => { + debug!(?error, "coerce: unsize failed"); + } + } + + // Examine the target type and consider type-specific coercions, such + // as auto-borrowing, coercing pointer mutability, or pin-ergonomics. + match *b.kind() { + ty::RawPtr(_, b_mutbl) => { + return self.coerce_to_raw_ptr(a, b, b_mutbl); + } + ty::Ref(r_b, _, mutbl_b) => { + return self.coerce_to_ref(a, b, r_b, mutbl_b); + } + ty::Adt(pin, _) + if self.tcx.features().pin_ergonomics() + && self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => + { + let pin_coerce = self.commit_if_ok(|_| self.coerce_to_pin_ref(a, b)); + if pin_coerce.is_ok() { + return pin_coerce; + } + } + _ => {} + } + + match *a.kind() { + ty::FnDef(..) => { + // Function items are coercible to any closure + // type; function pointers are not (that would + // require double indirection). + // Additionally, we permit coercion of function + // items to drop the unsafe qualifier. + self.coerce_from_fn_item(a, b) + } + ty::FnPtr(a_sig_tys, a_hdr) => { + // We permit coercion of fn pointers to drop the + // unsafe qualifier. + self.coerce_from_fn_pointer(a, a_sig_tys.with(a_hdr), b) + } + ty::Closure(..) => { + // Non-capturing closures are coercible to + // function pointers or unsafe function pointers. + // It cannot convert closures that require unsafe. + self.coerce_closure_to_fn(a, b) + } + _ => { + // Typically, we would fallback to unification rules, but we + // delay this on the *reverse* because it would succeed in + // more cases than we want (for example, opaques). + if unify_fallback { + self.unify(a, b, ForceLeakCheck::No) + } else { + Err(TypeError::Mismatch) + } + } + } + }; + + let result = self.commit_if_ok(|_| coerce_inner(new_ty, prev_ty, true)); + let first_error = match result { + Ok(forwd_result) => { + let prev_ty = self.resolve_vars_if_possible(prev_ty); + let new_ty = self.resolve_vars_if_possible(new_ty); + let rev_result = self.commit_if_ok(|_| coerce_inner(prev_ty, new_ty, false)); + if let Ok(rev_result) = rev_result { + let forwd_ty = self.resolve_vars_if_possible(forwd_result.value.1); + let rev_ty = self.resolve_vars_if_possible(rev_result.value.1); + let res = self.unify(forwd_ty, rev_ty, ForceLeakCheck::No); + if let Err(e) = res { + return Err(e); + } + } + + return Ok(InferOk { + obligations: forwd_result.obligations, + value: (forwd_result.value.0, forwd_result.value.1, true), + }); + } + Err(e) => e, + }; + + let result = self.commit_if_ok(|_| coerce_inner(prev_ty, new_ty, true)); + if let Ok(_) = result { + return result.map(|r| InferOk { + obligations: r.obligations, + value: (r.value.0, r.value.1, false), + }); + } + + Err(first_error) + } + /// Coercing *from* an inference variable. In this case, we have no information /// about the source type, so we can't really do a true coercion and we always /// fall back to subtyping (`unify_and`). @@ -632,6 +818,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ForceLeakCheck::No, )?; + tracing::debug!(?coercion); + // Create an obligation for `Source: CoerceUnsized`. let cause = self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target }); let pred = ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]); @@ -1085,7 +1273,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn sig_for_fn_def_coercion( + pub(crate) fn sig_for_fn_def_coercion( &self, fndef: Ty<'tcx>, expected_safety: Option, @@ -1128,7 +1316,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn sig_for_closure_coercion( + pub(crate) fn sig_for_closure_coercion( &self, closure: Ty<'tcx>, expected_safety: Option, @@ -1169,10 +1357,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn try_find_coercion_lub( &self, cause: &ObligationCause<'tcx>, - exprs: &[&'tcx hir::Expr<'tcx>], + exprs: &[(Option<&'tcx hir::Expr<'tcx>>, Ty<'tcx>)], prev_ty: Ty<'tcx>, - new: &hir::Expr<'_>, + new: &'tcx hir::Expr<'tcx>, new_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, ) -> RelateResult<'tcx, Ty<'tcx>> { let prev_ty = self.try_structurally_resolve_type(cause.span, prev_ty); let new_ty = self.try_structurally_resolve_type(new.span, new_ty); @@ -1267,7 +1456,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer(sig.safety())), _ => span_bug!(new.span, "should not try to coerce a {new_ty} to a fn pointer"), }; - for expr in exprs.iter() { + for expr in exprs.iter().filter_map(|e| e.0) { self.apply_adjustments( expr, vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }], @@ -1277,6 +1466,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ok(fn_ptr); } + // This might be okay, but we previously branched on this without any + // test, so I'm just keeping the assert to avoid surprising behavior. + assert!(!self.typeck_results.borrow().adjustments().contains_key(new.hir_id)); + // Configure a Coerce instance to compute the LUB. // We don't allow two-phase borrows on any autorefs this creates since we // probably aren't processing function arguments here and even if we were, @@ -1288,38 +1481,41 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No, true); coerce.use_lub = true; - // First try to coerce the new expression to the type of the previous ones, - // but only if the new expression has no coercion already applied to it. - let mut first_error = None; - if !self.typeck_results.borrow().adjustments().contains_key(new.hir_id) { - let result = self.commit_if_ok(|_| coerce.coerce(new_ty, prev_ty)); - match result { - Ok(ok) => { - let (adjustments, target) = self.register_infer_ok_obligations(ok); + let target = match coerce.coerce_mutual(prev_ty, new_ty) { + Ok(res) => { + let (adjustments, target, prev_ty_chosen) = self.register_infer_ok_obligations(res); + if prev_ty_chosen { self.apply_adjustments(new, adjustments); debug!( "coercion::try_find_coercion_lub: was able to coerce from new type {:?} to previous type {:?} ({:?})", new_ty, prev_ty, target ); - return Ok(target); + } else { + for (expr, expr_ty) in exprs { + let ok = self.commit_if_ok(|_| coerce.coerce(*expr_ty, new_ty))?; + let (adj, _) = self.register_infer_ok_obligations(ok); + debug!(?expr_ty, ?new_ty); + if let Some(expr) = expr { + self.set_adjustments(*expr, adj); + } + } + debug!( + "coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})", + prev_ty, new_ty, target + ); } - Err(e) => first_error = Some(e), + target } - } - - let ok = self - .commit_if_ok(|_| coerce.coerce(prev_ty, new_ty)) - // Avoid giving strange errors on failed attempts. - .map_err(|e| first_error.unwrap_or(e))?; + Err(coerce_mutual_err) => { + let new_to_expected_res = + self.coerce(new, new_ty, expected_ty, AllowTwoPhase::No, Some(cause.clone())); + match new_to_expected_res { + Ok(t) => t, + Err(_) => return Err(coerce_mutual_err), + } + } + }; - let (adjustments, target) = self.register_infer_ok_obligations(ok); - for expr in exprs { - self.apply_adjustments(expr, adjustments.clone()); - } - debug!( - "coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})", - prev_ty, new_ty, target - ); Ok(target) } } @@ -1405,7 +1601,8 @@ pub fn can_coerce<'tcx>( pub(crate) struct CoerceMany<'tcx> { expected_ty: Ty<'tcx>, final_ty: Option>, - expressions: Vec<&'tcx hir::Expr<'tcx>>, + expressions: Vec<(Option<&'tcx hir::Expr<'tcx>>, Ty<'tcx>)>, + pub(crate) initial_guidance_only: bool, } impl<'tcx> CoerceMany<'tcx> { @@ -1417,8 +1614,14 @@ impl<'tcx> CoerceMany<'tcx> { } /// Creates a `CoerceMany` with a given capacity. + #[tracing::instrument()] pub(crate) fn with_capacity(expected_ty: Ty<'tcx>, capacity: usize) -> Self { - CoerceMany { expected_ty, final_ty: None, expressions: Vec::with_capacity(capacity) } + CoerceMany { + expected_ty, + final_ty: None, + expressions: Vec::with_capacity(capacity), + initial_guidance_only: false, + } } /// Returns the "expected type" with which this coercion was @@ -1535,13 +1738,44 @@ impl<'tcx> CoerceMany<'tcx> { // Special-case the first expression we are coercing. // To be honest, I'm not entirely sure why we do this. // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why - fcx.coerce( - expression, - expression_ty, - self.expected_ty, - AllowTwoPhase::No, - Some(cause.clone()), - ) + if !self.initial_guidance_only { + fcx.coerce( + expression, + expression_ty, + self.expected_ty, + AllowTwoPhase::No, + Some(cause.clone()), + ) + } else if false { + Ok(expression_ty) + } else { + // See `tests/ui/coercion/expected-guidance.rs` for why this is needed. + // If the expected type is e.g. `Option` and the first arm match sets it to `Option`, the + // second arm should be able to observe that `?0` is `X`. + + let source = fcx.try_structurally_resolve_type(expression.span, expression_ty); + let target = if fcx.next_trait_solver() { + fcx.try_structurally_resolve_type(cause.span, self.expected_ty) + } else { + self.expected_ty + }; + let coerce_never = + fcx.tcx.expr_guaranteed_to_constitute_read_for_never(expression); + let guidance = crate::coercion_guidance::CoerceGuidance::new( + fcx, + cause.clone(), + AllowTwoPhase::No, + coerce_never, + ) + .do_guidance(source, target); + match guidance { + Ok(ok) => { + fcx.register_infer_ok_obligations(ok); + Ok(source) + } + Err(e) => Err(e), + } + } } else { fcx.try_find_coercion_lub( cause, @@ -1549,6 +1783,7 @@ impl<'tcx> CoerceMany<'tcx> { self.merged_ty(), expression, expression_ty, + self.expected_ty, ) } } else { @@ -1566,6 +1801,33 @@ impl<'tcx> CoerceMany<'tcx> { // // Another example is `break` with no argument expression. assert!(expression_ty.is_unit(), "if let hack without unit type"); + for (expr, expr_ty) in self.expressions.iter() { + let coerce = Coerce::new(fcx, cause.clone(), AllowTwoPhase::No, true); + let ok = match fcx.commit_if_ok(|_| coerce.coerce(*expr_ty, expression_ty)) { + Ok(coerce) => coerce, + Err(e) => { + let reported = self.report_coercion_error( + fcx, + e, + cause, + expression, + expected, + found, + augment_error, + ); + self.final_ty = Some(Ty::new_error(fcx.tcx, reported)); + self.expressions.push((expression, expression_ty)); + return; + } + }; + let (adjustments, _) = fcx.register_infer_ok_obligations(ok); + if let Some(expr) = expr { + fcx.set_adjustments(expr, adjustments); + } + } + Ok(expression_ty) + + /* fcx.at(cause, fcx.param_env) .eq( // needed for tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs @@ -1577,179 +1839,194 @@ impl<'tcx> CoerceMany<'tcx> { fcx.register_infer_ok_obligations(infer_ok); expression_ty }) + */ }; debug!(?result); match result { Ok(v) => { self.final_ty = Some(v); - if let Some(e) = expression { - self.expressions.push(e); - } + self.expressions.push((expression, expression_ty)); } Err(coercion_error) => { - // Mark that we've failed to coerce the types here to suppress - // any superfluous errors we might encounter while trying to - // emit or provide suggestions on how to fix the initial error. - fcx.set_tainted_by_errors( - fcx.dcx().span_delayed_bug(cause.span, "coercion error but no error emitted"), + let reported = self.report_coercion_error( + fcx, + coercion_error, + cause, + expression, + expected, + found, + augment_error, ); - let (expected, found) = fcx.resolve_vars_if_possible((expected, found)); - - let mut err; - let mut unsized_return = false; - match *cause.code() { - ObligationCauseCode::ReturnNoExpression => { - err = struct_span_code_err!( - fcx.dcx(), - cause.span, - E0069, - "`return;` in a function whose return type is not `()`" - ); - if let Some(value) = fcx.err_ctxt().ty_kind_suggestion(fcx.param_env, found) - { - err.span_suggestion_verbose( - cause.span.shrink_to_hi(), - "give the `return` a value of the expected type", - format!(" {value}"), - Applicability::HasPlaceholders, - ); - } - err.span_label(cause.span, "return type is not `()`"); - } - ObligationCauseCode::BlockTailExpression(blk_id, ..) => { - err = self.report_return_mismatched_types( - cause, - expected, - found, - coercion_error, - fcx, - blk_id, - expression, - ); - unsized_return = self.is_return_ty_definitely_unsized(fcx); - } - ObligationCauseCode::ReturnValue(return_expr_id) => { - err = self.report_return_mismatched_types( - cause, - expected, - found, - coercion_error, - fcx, - return_expr_id, - expression, - ); - unsized_return = self.is_return_ty_definitely_unsized(fcx); - } - ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { - arm_span, + self.final_ty = Some(Ty::new_error(fcx.tcx, reported)); + } + } + } + + fn report_coercion_error( + &self, + fcx: &FnCtxt<'_, 'tcx>, + coercion_error: TypeError<'tcx>, + cause: &ObligationCause<'tcx>, + expression: Option<&'tcx hir::Expr<'tcx>>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + augment_error: impl FnOnce(&mut Diag<'_>), + ) -> ErrorGuaranteed { + // Mark that we've failed to coerce the types here to suppress + // any superfluous errors we might encounter while trying to + // emit or provide suggestions on how to fix the initial error. + fcx.set_tainted_by_errors( + fcx.dcx().span_delayed_bug(cause.span, "coercion error but no error emitted"), + ); + let (expected, found) = fcx.resolve_vars_if_possible((expected, found)); + + let mut err; + let mut unsized_return = false; + match *cause.code() { + ObligationCauseCode::ReturnNoExpression => { + err = struct_span_code_err!( + fcx.dcx(), + cause.span, + E0069, + "`return;` in a function whose return type is not `()`" + ); + if let Some(value) = fcx.err_ctxt().ty_kind_suggestion(fcx.param_env, found) { + err.span_suggestion_verbose( + cause.span.shrink_to_hi(), + "give the `return` a value of the expected type", + format!(" {value}"), + Applicability::HasPlaceholders, + ); + } + err.span_label(cause.span, "return type is not `()`"); + } + ObligationCauseCode::BlockTailExpression(blk_id, ..) => { + err = self.report_return_mismatched_types( + cause, + expected, + found, + coercion_error, + fcx, + blk_id, + expression, + ); + unsized_return = self.is_return_ty_definitely_unsized(fcx); + } + ObligationCauseCode::ReturnValue(return_expr_id) => { + err = self.report_return_mismatched_types( + cause, + expected, + found, + coercion_error, + fcx, + return_expr_id, + expression, + ); + unsized_return = self.is_return_ty_definitely_unsized(fcx); + } + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + arm_span, + arm_ty, + prior_arm_ty, + ref prior_non_diverging_arms, + tail_defines_return_position_impl_trait: Some(rpit_def_id), + .. + }) => { + err = fcx.err_ctxt().report_mismatched_types( + cause, + fcx.param_env, + expected, + found, + coercion_error, + ); + // Check that we're actually in the second or later arm + if prior_non_diverging_arms.len() > 0 { + self.suggest_boxing_tail_for_return_position_impl_trait( + fcx, + &mut err, + rpit_def_id, arm_ty, prior_arm_ty, - ref prior_non_diverging_arms, - tail_defines_return_position_impl_trait: Some(rpit_def_id), - .. - }) => { - err = fcx.err_ctxt().report_mismatched_types( - cause, - fcx.param_env, - expected, - found, - coercion_error, - ); - // Check that we're actually in the second or later arm - if prior_non_diverging_arms.len() > 0 { - self.suggest_boxing_tail_for_return_position_impl_trait( - fcx, - &mut err, - rpit_def_id, - arm_ty, - prior_arm_ty, - prior_non_diverging_arms - .iter() - .chain(std::iter::once(&arm_span)) - .copied(), - ); - } - } - ObligationCauseCode::IfExpression { - expr_id, - tail_defines_return_position_impl_trait: Some(rpit_def_id), - } => { - let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::If(_, then_expr, Some(else_expr)), - .. - }) = fcx.tcx.hir_node(expr_id) - else { - unreachable!(); - }; - err = fcx.err_ctxt().report_mismatched_types( - cause, - fcx.param_env, - expected, - found, - coercion_error, - ); - let then_span = fcx.find_block_span_from_hir_id(then_expr.hir_id); - let else_span = fcx.find_block_span_from_hir_id(else_expr.hir_id); - // Don't suggest wrapping whole block in `Box::new`. - if then_span != then_expr.span && else_span != else_expr.span { - let then_ty = fcx.typeck_results.borrow().expr_ty(then_expr); - let else_ty = fcx.typeck_results.borrow().expr_ty(else_expr); - self.suggest_boxing_tail_for_return_position_impl_trait( - fcx, - &mut err, - rpit_def_id, - then_ty, - else_ty, - [then_span, else_span].into_iter(), - ); - } - } - _ => { - err = fcx.err_ctxt().report_mismatched_types( - cause, - fcx.param_env, - expected, - found, - coercion_error, - ); - } + prior_non_diverging_arms.iter().chain(std::iter::once(&arm_span)).copied(), + ); } - - augment_error(&mut err); - - if let Some(expr) = expression { - if let hir::ExprKind::Loop( - _, - _, - loop_src @ (hir::LoopSource::While | hir::LoopSource::ForLoop), - _, - ) = expr.kind - { - let loop_type = if loop_src == hir::LoopSource::While { - "`while` loops" - } else { - "`for` loops" - }; - - err.note(format!("{loop_type} evaluate to unit type `()`")); - } - - fcx.emit_coerce_suggestions( + } + ObligationCauseCode::IfExpression { + expr_id, + tail_defines_return_position_impl_trait: Some(rpit_def_id), + } => { + let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::If(_, then_expr, Some(else_expr)), + .. + }) = fcx.tcx.hir_node(expr_id) + else { + unreachable!(); + }; + err = fcx.err_ctxt().report_mismatched_types( + cause, + fcx.param_env, + expected, + found, + coercion_error, + ); + let then_span = fcx.find_block_span_from_hir_id(then_expr.hir_id); + let else_span = fcx.find_block_span_from_hir_id(else_expr.hir_id); + // Don't suggest wrapping whole block in `Box::new`. + if then_span != then_expr.span && else_span != else_expr.span { + let then_ty = fcx.typeck_results.borrow().expr_ty(then_expr); + let else_ty = fcx.typeck_results.borrow().expr_ty(else_expr); + self.suggest_boxing_tail_for_return_position_impl_trait( + fcx, &mut err, - expr, - found, - expected, - None, - Some(coercion_error), + rpit_def_id, + then_ty, + else_ty, + [then_span, else_span].into_iter(), ); } + } + _ => { + err = fcx.err_ctxt().report_mismatched_types( + cause, + fcx.param_env, + expected, + found, + coercion_error, + ); + } + } - let reported = err.emit_unless_delay(unsized_return); + augment_error(&mut err); - self.final_ty = Some(Ty::new_error(fcx.tcx, reported)); + if let Some(expr) = expression { + if let hir::ExprKind::Loop( + _, + _, + loop_src @ (hir::LoopSource::While | hir::LoopSource::ForLoop), + _, + ) = expr.kind + { + let loop_type = if loop_src == hir::LoopSource::While { + "`while` loops" + } else { + "`for` loops" + }; + + err.note(format!("{loop_type} evaluate to unit type `()`")); } + + fcx.emit_coerce_suggestions( + &mut err, + expr, + found, + expected, + None, + Some(coercion_error), + ); } + + err.emit_unless_delay(unsized_return) } fn suggest_boxing_tail_for_return_position_impl_trait( @@ -1943,14 +2220,97 @@ impl<'tcx> CoerceMany<'tcx> { } } - pub(crate) fn complete<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Ty<'tcx> { - if let Some(final_ty) = self.final_ty { - final_ty - } else { + #[tracing::instrument(skip(self, fcx), ret)] + pub(crate) fn complete<'a>( + &self, + fcx: &FnCtxt<'a, 'tcx>, + cause: &ObligationCause<'tcx>, + mut expected_ty: Ty<'tcx>, + coerce_never: bool, + ) -> Ty<'tcx> { + let Some(final_ty) = self.final_ty else { // If we only had inputs that were of type `!` (or no // inputs at all), then the final type is `!`. assert!(self.expressions.is_empty()); - fcx.tcx.types.never + return fcx.tcx.types.never; + }; + + if !self.initial_guidance_only { + return final_ty; + } + + let mut expected_ty = self.expected_ty; + if fcx.next_trait_solver() { + expected_ty = fcx.try_structurally_resolve_type(cause.span, expected_ty); + } + + if fcx.next_trait_solver() { + expected_ty = fcx.try_structurally_resolve_type(cause.span, expected_ty); + } + + let final_ty = fcx.try_structurally_resolve_type(cause.span, final_ty); + let expected_ty = fcx.try_structurally_resolve_type(cause.span, expected_ty); + debug!("coerce::complete (final_ty): {:?} -> {:?}", final_ty, expected_ty); + + if true { + // You may ask "Why do we coerce the `final_ty` to the `expected_ty`, and + // then *also* each expression ty to the `expected_ty`?". + // TODO: investigate and validate the following claim + // Basically, if `expected_ty` is an inference variable, then a coercion + // with the first arm can constrain that because of the `unify` fallback. + + let coerce = Coerce::new(fcx, cause.clone(), AllowTwoPhase::No, coerce_never); + let ok = match fcx.commit_if_ok(|_| coerce.coerce(final_ty, expected_ty)) { + Ok(coerce) => coerce, + Err(err) => { + let reported = self.report_coercion_error( + fcx, + err, + cause, + None, + expected_ty, + final_ty, + |_| {}, + ); + return Ty::new_error(fcx.tcx, reported); + } + }; + let _ = fcx.register_infer_ok_obligations(ok); + } + + for (expr, ty) in self.expressions.iter() { + let source = fcx.try_structurally_resolve_type(cause.span, *ty); + debug!("coerce::complete (expression): {:?} -> {:?}", source, expected_ty); + + let coerce = Coerce::new(fcx, cause.clone(), AllowTwoPhase::No, coerce_never); + let ok = match fcx.commit_if_ok(|_| coerce.coerce(source, expected_ty)) { + Ok(coerce) => coerce, + Err(e) => { + let reported = self.report_coercion_error( + fcx, + e, + cause, + *expr, + expected_ty, + source, + |_| {}, + ); + return Ty::new_error(fcx.tcx, reported); + } + }; + let (adjustments, _) = fcx.register_infer_ok_obligations(ok); + + if let Some(expr) = expr { + fcx.set_adjustments(expr, adjustments.clone()); + } + } + + let final_ty = fcx.try_structurally_resolve_type(cause.span, final_ty); + + if let Err(guar) = final_ty.error_reported() { + Ty::new_error(fcx.tcx, guar) + } else { + expected_ty } } } @@ -1958,13 +2318,13 @@ impl<'tcx> CoerceMany<'tcx> { /// Recursively visit goals to decide whether an unsizing is possible. /// `Break`s when it isn't, and an error should be raised. /// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item. -struct CoerceVisitor<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - span: Span, +pub(crate) struct CoerceVisitor<'a, 'tcx> { + pub(crate) fcx: &'a FnCtxt<'a, 'tcx>, + pub(crate) span: Span, /// Whether the coercion is impossible. If so we sometimes still try to /// coerce in these cases to emit better errors. This changes the behavior /// when hitting the recursion limit. - errored: bool, + pub(crate) errored: bool, } impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> { diff --git a/compiler/rustc_hir_typeck/src/coercion_guidance.rs b/compiler/rustc_hir_typeck/src/coercion_guidance.rs new file mode 100644 index 0000000000000..1c03af6097c19 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/coercion_guidance.rs @@ -0,0 +1,856 @@ +//! # Type Coercion +//! +//! Under certain circumstances we will coerce from one type to another, +//! for example by auto-borrowing. This occurs in situations where the +//! compiler has a firm 'expected type' that was supplied from the user, +//! and where the actual type is similar to that expected type in purpose +//! but not in representation (so actual subtyping is inappropriate). +//! +//! ## Reborrowing +//! +//! Note that if we are expecting a reference, we will *reborrow* +//! even if the argument provided was already a reference. This is +//! useful for freezing mut things (that is, when the expected type is &T +//! but you have &mut T) and also for avoiding the linearity +//! of mut things (when the expected is &mut T and you have &mut T). See +//! the various `tests/ui/coerce/*.rs` tests for +//! examples of where this is useful. +//! +//! ## Subtle note +//! +//! When inferring the generic arguments of functions, the argument +//! order is relevant, which can lead to the following edge case: +//! +//! ```ignore (illustrative) +//! fn foo(a: T, b: T) { +//! // ... +//! } +//! +//! foo(&7i32, &mut 7i32); +//! // This compiles, as we first infer `T` to be `&i32`, +//! // and then coerce `&mut 7i32` to `&7i32`. +//! +//! foo(&mut 7i32, &7i32); +//! // This does not compile, as we first infer `T` to be `&mut i32` +//! // and are then unable to coerce `&7i32` to `&mut i32`. +//! ``` + +use std::ops::Deref; + +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir}; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; +use rustc_infer::infer::relate::RelateResult; +use rustc_infer::infer::{ + DefineOpaqueTypes, InferCtxt, InferOk, InferResult, RegionVariableOrigin, +}; +use rustc_infer::traits::{Obligation, PredicateObligation, PredicateObligations, SelectionError}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, +}; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::relate::{self, Relate, TypeRelation}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_trait_selection::solve::Goal; +use rustc_trait_selection::solve::inspect::InferCtxtProofTreeExt; +use rustc_trait_selection::traits::{ + self, ImplSource, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt, +}; +use smallvec::{SmallVec, smallvec}; +use tracing::{debug, instrument}; + +use crate::FnCtxt; +use crate::coercion::CoerceVisitor; + +pub(crate) struct CoerceGuidance<'a, 'tcx> { + fcx: &'a FnCtxt<'a, 'tcx>, + cause: ObligationCause<'tcx>, + use_lub: bool, + /// Determines whether or not allow_two_phase_borrow is set on any + /// autoref adjustments we create while coercing. We don't want to + /// allow deref coercions to create two-phase borrows, at least initially, + /// but we do need two-phase borrows for function argument reborrows. + /// See #47489 and #48598 + /// See docs on the "AllowTwoPhase" type for a more detailed discussion + allow_two_phase: AllowTwoPhase, + /// Whether we allow `NeverToAny` coercions. This is unsound if we're + /// coercing a place expression without it counting as a read in the MIR. + /// This is a side-effect of HIR not really having a great distinction + /// between places and values. + coerce_never: bool, +} + +impl<'a, 'tcx> Deref for CoerceGuidance<'a, 'tcx> { + type Target = FnCtxt<'a, 'tcx>; + fn deref(&self) -> &Self::Target { + self.fcx + } +} + +/// Coercing a mutable reference to an immutable works, while +/// coercing `&T` to `&mut T` should be forbidden. +fn coerce_mutbls<'tcx>( + from_mutbl: hir::Mutability, + to_mutbl: hir::Mutability, +) -> RelateResult<'tcx, ()> { + if from_mutbl >= to_mutbl { Ok(()) } else { Err(TypeError::Mutability) } +} + +#[derive(Debug)] +enum ForceLeakCheck { + Yes, + No, +} + +type CoerceGuidanceResult<'tcx> = InferResult<'tcx, ()>; + +impl<'f, 'tcx> CoerceGuidance<'f, 'tcx> { + pub(crate) fn new( + fcx: &'f FnCtxt<'f, 'tcx>, + cause: ObligationCause<'tcx>, + allow_two_phase: AllowTwoPhase, + coerce_never: bool, + ) -> Self { + CoerceGuidance { fcx, cause, allow_two_phase, use_lub: false, coerce_never } + } + + #[tracing::instrument(skip(self), ret)] + fn unify( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + leak_check: ForceLeakCheck, + ) -> CoerceGuidanceResult<'tcx> { + debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub); + let guidance = TypeGuidance { fcx: self.fcx }.relate(a, b)?; + let at = self.at(&self.cause, self.fcx.param_env); + at.sup(DefineOpaqueTypes::Yes, b, a) + } + + #[instrument(skip(self), ret)] + pub(crate) fn do_guidance( + &self, + source: Ty<'tcx>, + target: Ty<'tcx>, + ) -> CoerceGuidanceResult<'tcx> { + // First, remove any resolved type variables (at the top level, at least): + let source = self.shallow_resolve(source); + let target = self.shallow_resolve(target); + debug!("CoerceGuidance.tys({:?} => {:?})", source, target); + + // Coercing from `!` to any type is allowed, but gives no guidance + if source.is_never() { + if self.coerce_never { + return Ok(InferOk { value: (), obligations: PredicateObligations::new() }); + } else { + // Otherwise the only coercion we can do is unification. + return self.unify(source, target, ForceLeakCheck::No); + } + } + + // Coercing *from* an unresolved inference variable means that + // we have no information about the source type. This will always + // ultimately fall back to some form of subtyping. + if source.is_ty_var() { + return Ok(InferOk { value: (), obligations: PredicateObligations::new() }); + } + + // Consider coercing the subtype to a DST + // + // NOTE: this is wrapped in a `commit_if_ok` because it creates + // a "spurious" type variable, and we don't want to have that + // type variable in memory if the coercion fails. + let unsize = self.commit_if_ok(|_| self.coerce_unsized(source, target)); + match unsize { + Ok(_) => { + debug!("coerce: unsize successful"); + return unsize; + } + Err(error) => { + debug!(?error, "coerce: unsize failed"); + } + } + + // Examine the target type and consider type-specific coercions, such + // as auto-borrowing, coercing pointer mutability, or pin-ergonomics. + match *target.kind() { + ty::RawPtr(_, b_mutbl) => { + return self.coerce_to_raw_ptr(source, target, b_mutbl); + } + ty::Ref(r_b, _, mutbl_b) => { + return self.coerce_to_ref(source, target, r_b, mutbl_b); + } + ty::Adt(pin, _) + if self.tcx.features().pin_ergonomics() + && self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => + { + let pin_coerce = self.commit_if_ok(|_| self.coerce_to_pin_ref(source, target)); + if pin_coerce.is_ok() { + return pin_coerce; + } + } + _ => {} + } + + match *source.kind() { + ty::FnDef(..) => { + // Function items are coercible to any closure + // type; function pointers are not (that would + // require double indirection). + // Additionally, we permit coercion of function + // items to drop the unsafe qualifier. + self.coerce_from_fn_item(source, target) + } + ty::FnPtr(a_sig_tys, a_hdr) => { + // We permit coercion of fn pointers to drop the + // unsafe qualifier. + self.coerce_from_fn_pointer(source, a_sig_tys.with(a_hdr), target) + } + ty::Closure(..) => { + // Non-capturing closures are coercible to + // function pointers or unsafe function pointers. + // It cannot convert closures that require unsafe. + self.coerce_closure_to_fn(source, target) + } + _ => { + let source = self.fcx.try_structurally_resolve_type(rustc_span::DUMMY_SP, source); + let target = if self.fcx.next_trait_solver() { + self.fcx.try_structurally_resolve_type(rustc_span::DUMMY_SP, target) + } else { + target + }; + + // Otherwise, just use unification rules. + self.unify(source, target, ForceLeakCheck::No) + } + } + } + + /// Handles coercing some arbitrary type `a` to some reference (`b`). This + /// handles a few cases: + /// - Introducing reborrows to give more flexible lifetimes + /// - Deref coercions to allow `&T` to coerce to `&T::Target` + /// - Coercing mutable references to immutable references + /// These coercions can be freely intermixed, for example we are able to + /// coerce `&mut T` to `&mut T::Target`. + fn coerce_to_ref( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + r_b: ty::Region<'tcx>, + mutbl_b: hir::Mutability, + ) -> CoerceGuidanceResult<'tcx> { + debug!("coerce_to_ref(a={:?}, b={:?})", a, b); + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + + let (r_a, mt_a) = match *a.kind() { + ty::Ref(r_a, ty, mutbl) => { + coerce_mutbls(mutbl, mutbl_b)?; + (r_a, ty::TypeAndMut { ty, mutbl }) + } + _ => return self.unify(a, b, ForceLeakCheck::No), + }; + + // Look at each step in the `Deref` chain and check if + // any of the autoref'd `Target` types unify with the + // coercion target. + // + // For example when coercing from `&mut Vec` to `&M [T]` we + // have three deref steps: + // 1. `&mut Vec`, skip autoref + // 2. `Vec`, autoref'd ty: `&M Vec` + // - `&M Vec` does not unify with `&M [T]` + // 3. `[T]`, autoref'd ty: `&M [T]` + // - `&M [T]` does unify with `&M [T]` + let mut first_error = None; + let mut r_borrow_var = None; + let mut autoderef = self.autoderef(self.cause.span, a); + let found = autoderef.by_ref().find_map(|(deref_ty, autoderefs)| { + if autoderefs == 0 { + // Don't autoref the first step as otherwise we'd allow + // coercing `&T` to `&&T`. + return None; + } + + // The logic here really shouldn't exist. We don't care about free + // lifetimes during HIR typeck. Unfortunately later parts of this + // function rely on structural identity of the autoref'd deref'd ty. + // + // This means that what region we use here actually impacts whether + // we emit a reborrow coercion or not which can affect diagnostics + // and capture analysis (which in turn affects borrowck). + let r = if !self.use_lub { + r_b + } else if autoderefs == 1 { + r_a + } else { + if r_borrow_var.is_none() { + // create var lazily, at most once + let coercion = RegionVariableOrigin::Coercion(self.cause.span); + let r = self.next_region_var(coercion); + r_borrow_var = Some(r); + } + r_borrow_var.unwrap() + }; + + let autorefd_deref_ty = Ty::new_ref(self.tcx, r, deref_ty, mutbl_b); + + // Note that we unify the autoref'd `Target` type with `b` rather than + // the `Target` type with the pointee of `b`. This is necessary + // to properly account for the differing variances of the pointees + // of `&` vs `&mut` references. + match self.unify(autorefd_deref_ty, b, ForceLeakCheck::No) { + Ok(ok) => Some(ok), + Err(err) => { + if first_error.is_none() { + first_error = Some(err); + } + None + } + } + }); + + // Extract type or return an error. We return the first error + // we got, which should be from relating the "base" type + // (e.g., in example above, the failure from relating `Vec` + // to the target type), since that should be the least + // confusing. + let Some(InferOk { value: (), mut obligations }) = found else { + if let Some(first_error) = first_error { + debug!("coerce_to_ref: failed with err = {:?}", first_error); + return Err(first_error); + } else { + // This may happen in the new trait solver since autoderef requires + // the pointee to be structurally normalizable, or else it'll just bail. + // So when we have a type like `&`, then we get no + // autoderef steps (even though there should be at least one). That means + // we get no type mismatches, since the loop above just exits early. + return Err(TypeError::Mismatch); + } + }; + + Ok(InferOk { value: (), obligations }) + } + + /// Performs [unsized coercion] by emulating a fulfillment loop on a + /// `CoerceUnsized` goal until all `CoerceUnsized` and `Unsize` goals + /// are successfully selected. + /// + /// [unsized coercion](https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions) + #[instrument(skip(self), level = "debug")] + fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceGuidanceResult<'tcx> { + debug!(?source, ?target); + debug_assert!(self.shallow_resolve(source) == source); + debug_assert!(self.shallow_resolve(target) == target); + + // We don't apply any coercions incase either the source or target + // aren't sufficiently well known but tend to instead just equate + // them both. + if source.is_ty_var() { + debug!("coerce_unsized: source is a TyVar, bailing out"); + return Err(TypeError::Mismatch); + } + if target.is_ty_var() { + debug!("coerce_unsized: target is a TyVar, bailing out"); + return Err(TypeError::Mismatch); + } + + // This is an optimization because coercion is one of the most common + // operations that we do in typeck, since it happens at every assignment + // and call arg (among other positions). + // + // These targets are known to never be RHS in `LHS: CoerceUnsized`. + // That's because these are built-in types for which a core-provided impl + // doesn't exist, and for which a user-written impl is invalid. + // + // This is technically incomplete when users write impossible bounds like + // `where T: CoerceUnsized`, for example, but that trait is unstable + // and coercion is allowed to be incomplete. The only case where this matters + // is impossible bounds. + // + // Note that some of these types implement `LHS: Unsize`, but they + // do not implement *`CoerceUnsized`* which is the root obligation of the + // check below. + match target.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::FnDef(_, _) + | ty::FnPtr(_, _) + | ty::Dynamic(_, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) + | ty::Never + | ty::Tuple(_) => return Err(TypeError::Mismatch), + _ => {} + } + // `&str: CoerceUnsized<&str>` does not hold but is encountered frequently + // so we fast path bail out here + if let ty::Ref(_, source_pointee, ty::Mutability::Not) = *source.kind() + && source_pointee.is_str() + && let ty::Ref(_, target_pointee, ty::Mutability::Not) = *target.kind() + && target_pointee.is_str() + { + return Err(TypeError::Mismatch); + } + + let traits = + (self.tcx.lang_items().unsize_trait(), self.tcx.lang_items().coerce_unsized_trait()); + let (Some(unsize_did), Some(coerce_unsized_did)) = traits else { + debug!("missing Unsize or CoerceUnsized traits"); + return Err(TypeError::Mismatch); + }; + + // Note, we want to avoid unnecessary unsizing. We don't want to coerce to + // a DST unless we have to. This currently comes out in the wash since + // we can't unify [T] with U. But to properly support DST, we need to allow + // that, at which point we will need extra checks on the target here. + + // Handle reborrows before selecting `Source: CoerceUnsized`. + let reborrow = match (source.kind(), target.kind()) { + (&ty::Ref(_, ty_a, mutbl_a), &ty::Ref(_, _, mutbl_b)) => { + coerce_mutbls(mutbl_a, mutbl_b)?; + + let coercion = RegionVariableOrigin::Coercion(self.cause.span); + let r_borrow = self.next_region_var(coercion); + + // We don't allow two-phase borrows here, at least for initial + // implementation. If it happens that this coercion is a function argument, + // the reborrow in coerce_borrowed_ptr will pick it up. + let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); + + Some(( + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, + Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), + target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b), + }, + )) + } + (&ty::Ref(_, ty_a, mt_a), &ty::RawPtr(_, mt_b)) => { + coerce_mutbls(mt_a, mt_b)?; + + Some(( + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, + Adjustment { + kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), + target: Ty::new_ptr(self.tcx, ty_a, mt_b), + }, + )) + } + _ => None, + }; + let coerce_source = reborrow.as_ref().map_or(source, |(_, r)| r.target); + + // Setup either a subtyping or a LUB relationship between + // the `CoerceUnsized` target type and the expected type. + // We only have the latter, so we use an inference variable + // for the former and let type inference do the rest. + let coerce_target = self.next_ty_var(self.cause.span); + + let mut coercion = self.unify(coerce_target, target, ForceLeakCheck::No)?; + + // Create an obligation for `Source: CoerceUnsized`. + let cause = self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target }); + let pred = ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]); + let obligation = Obligation::new(self.tcx, cause, self.fcx.param_env, pred); + + if self.next_trait_solver() { + if self + .infcx + .visit_proof_tree( + Goal::new(self.tcx, self.param_env, pred), + &mut CoerceVisitor { fcx: self.fcx, span: self.cause.span, errored: false }, + ) + .is_break() + { + return Err(TypeError::Mismatch); + } + } else { + self.coerce_unsized_old_solver(obligation, coerce_unsized_did, unsize_did)?; + } + + Ok(coercion) + } + + fn coerce_unsized_old_solver( + &self, + obligation: Obligation<'tcx, ty::Predicate<'tcx>>, + coerce_unsized_did: DefId, + unsize_did: DefId, + ) -> Result<(), TypeError<'tcx>> { + let mut selcx = traits::SelectionContext::new(self); + // Use a FIFO queue for this custom fulfillment procedure. + // + // A Vec (or SmallVec) is not a natural choice for a queue. However, + // this code path is hot, and this queue usually has a max length of 1 + // and almost never more than 3. By using a SmallVec we avoid an + // allocation, at the (very small) cost of (occasionally) having to + // shift subsequent elements down when removing the front element. + let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![obligation]; + + // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid + // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where + // inference might unify those two inner type variables later. + let traits = [coerce_unsized_did, unsize_did]; + while !queue.is_empty() { + let obligation = queue.remove(0); + let trait_pred = match obligation.predicate.kind().no_bound_vars() { + Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) + if traits.contains(&trait_pred.def_id()) => + { + self.resolve_vars_if_possible(trait_pred) + } + // Eagerly process alias-relate obligations in new trait solver, + // since these can be emitted in the process of solving trait goals, + // but we need to constrain vars before processing goals mentioning + // them. + Some(ty::PredicateKind::AliasRelate(..)) => { + let ocx = ObligationCtxt::new(self); + ocx.register_obligation(obligation); + if !ocx.try_evaluate_obligations().is_empty() { + return Err(TypeError::Mismatch); + } + continue; + } + _ => { + continue; + } + }; + debug!("coerce_unsized resolve step: {:?}", trait_pred); + match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) { + // Uncertain or unimplemented. + Ok(None) => { + if trait_pred.def_id() == unsize_did { + let self_ty = trait_pred.self_ty(); + let unsize_ty = trait_pred.trait_ref.args[1].expect_ty(); + debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); + match (self_ty.kind(), unsize_ty.kind()) { + (&ty::Infer(ty::TyVar(v)), ty::Dynamic(..)) + if self.type_var_is_sized(v) => + { + debug!("coerce_unsized: have sized infer {:?}", v); + // `$0: Unsize` where we know that `$0: Sized`, try going + // for unsizing. + } + _ => { + // Some other case for `$0: Unsize`. Note that we + // hit this case even if `Something` is a sized type, so just + // don't do the coercion. + debug!("coerce_unsized: ambiguous unsize"); + return Err(TypeError::Mismatch); + } + } + } else { + debug!("coerce_unsized: early return - ambiguous"); + return Err(TypeError::Mismatch); + } + } + Err(SelectionError::Unimplemented) => { + debug!("coerce_unsized: early return - can't prove obligation"); + return Err(TypeError::Mismatch); + } + + Err(SelectionError::TraitDynIncompatible(_)) => { + // Dyn compatibility errors in coercion will *always* be due to the + // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait` + // written in source somewhere (otherwise we will never have lowered + // the dyn trait from HIR to middle). + // + // There's no reason to emit yet another dyn compatibility error, + // especially since the span will differ slightly and thus not be + // deduplicated at all! + self.fcx.set_tainted_by_errors( + self.fcx + .dcx() + .span_delayed_bug(self.cause.span, "dyn compatibility during coercion"), + ); + } + Err(err) => { + let guar = self.err_ctxt().report_selection_error( + obligation.clone(), + &obligation, + &err, + ); + self.fcx.set_tainted_by_errors(guar); + // Treat this like an obligation and follow through + // with the unsizing - the lack of a coercion should + // be silent, as it causes a type mismatch later. + } + Ok(Some(ImplSource::UserDefined(impl_source))) => { + queue.extend(impl_source.nested); + // Certain incoherent `CoerceUnsized` implementations may cause ICEs, + // so check the impl's validity. Taint the body so that we don't try + // to evaluate these invalid coercions in CTFE. We only need to do this + // for local impls, since upstream impls should be valid. + if impl_source.impl_def_id.is_local() + && let Err(guar) = + self.tcx.ensure_ok().coerce_unsized_info(impl_source.impl_def_id) + { + self.fcx.set_tainted_by_errors(guar); + } + } + Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), + } + } + + Ok(()) + } + + /// Applies reborrowing for `Pin` + /// + /// We currently only support reborrowing `Pin<&mut T>` as `Pin<&mut T>`. This is accomplished + /// by inserting a call to `Pin::as_mut` during MIR building. + /// + /// In the future we might want to support other reborrowing coercions, such as: + /// - `Pin<&mut T>` as `Pin<&T>` + /// - `Pin<&T>` as `Pin<&T>` + /// - `Pin>` as `Pin<&T>` + /// - `Pin>` as `Pin<&mut T>` + #[instrument(skip(self), level = "trace")] + fn coerce_to_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceGuidanceResult<'tcx> { + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + + // We need to make sure the two types are compatible for coercion. + // Then we will build a ReborrowPin adjustment and return that as an InferOk. + + // Right now we can only reborrow if this is a `Pin<&mut T>`. + let extract_pin_mut = |ty: Ty<'tcx>| { + // Get the T out of Pin + let (pin, ty) = match ty.kind() { + ty::Adt(pin, args) if self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => { + (*pin, args[0].expect_ty()) + } + _ => { + debug!("can't reborrow {:?} as pinned", ty); + return Err(TypeError::Mismatch); + } + }; + // Make sure the T is something we understand (just `&mut U` for now) + match ty.kind() { + ty::Ref(region, ty, mutbl) => Ok((pin, *region, *ty, *mutbl)), + _ => { + debug!("can't reborrow pin of inner type {:?}", ty); + Err(TypeError::Mismatch) + } + } + }; + + let (pin, a_region, a_ty, mut_a) = extract_pin_mut(a)?; + let (_, _, _b_ty, mut_b) = extract_pin_mut(b)?; + + coerce_mutbls(mut_a, mut_b)?; + + // update a with b's mutability since we'll be coercing mutability + let a = Ty::new_adt( + self.tcx, + pin, + self.tcx.mk_args(&[Ty::new_ref(self.tcx, a_region, a_ty, mut_b).into()]), + ); + + // To complete the reborrow, we need to make sure we can unify the inner types, and if so we + // add the adjustments. + self.unify(a, b, ForceLeakCheck::No) + } + + fn coerce_from_fn_pointer( + &self, + a: Ty<'tcx>, + a_sig: ty::PolyFnSig<'tcx>, + b: Ty<'tcx>, + ) -> CoerceGuidanceResult<'tcx> { + debug!(?a_sig, ?b, "coerce_from_fn_pointer"); + debug_assert!(self.shallow_resolve(b) == b); + + match b.kind() { + ty::FnPtr(_, b_hdr) if a_sig.safety().is_safe() && b_hdr.safety.is_unsafe() => { + let a = self.tcx.safe_to_unsafe_fn_ty(a_sig); + self.unify(a, b, ForceLeakCheck::Yes) + } + _ => self.unify(a, b, ForceLeakCheck::Yes), + } + } + + fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceGuidanceResult<'tcx> { + debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + + match b.kind() { + ty::FnPtr(_, b_hdr) => { + let a_sig = self.sig_for_fn_def_coercion(a, Some(b_hdr.safety))?; + + let InferOk { value: a_sig, obligations: _ } = + self.at(&self.cause, self.param_env).normalize(a_sig); + let a = Ty::new_fn_ptr(self.tcx, a_sig); + + self.unify(a, b, ForceLeakCheck::Yes) + } + _ => self.unify(a, b, ForceLeakCheck::No), + } + } + + /// Attempts to coerce from a closure to a function pointer. Fails + /// if the closure has any upvars. + fn coerce_closure_to_fn(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceGuidanceResult<'tcx> { + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + + match b.kind() { + ty::FnPtr(_, hdr) => { + let terr = TypeError::Sorts(ty::error::ExpectedFound::new(a, b)); + let closure_sig = self.sig_for_closure_coercion(a, Some(hdr.safety), terr)?; + let pointer_ty = Ty::new_fn_ptr(self.tcx, closure_sig); + debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty); + + self.unify(pointer_ty, b, ForceLeakCheck::No) + } + _ => self.unify(a, b, ForceLeakCheck::No), + } + } + + fn coerce_to_raw_ptr( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + mutbl_b: hir::Mutability, + ) -> CoerceGuidanceResult<'tcx> { + debug!("coerce_to_raw_ptr(a={:?}, b={:?})", a, b); + debug_assert!(self.shallow_resolve(a) == a); + debug_assert!(self.shallow_resolve(b) == b); + + let (is_ref, mt_a) = match *a.kind() { + ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }), + ty::RawPtr(ty, mutbl) => (false, ty::TypeAndMut { ty, mutbl }), + _ => return self.unify(a, b, ForceLeakCheck::No), + }; + coerce_mutbls(mt_a.mutbl, mutbl_b)?; + + // Check that the types which they point at are compatible. + let a_raw = Ty::new_ptr(self.tcx, mt_a.ty, mutbl_b); + // Although references and raw ptrs have the same + // representation, we still register an Adjust::DerefRef so that + // regionck knows that the region for `a` must be valid here. + if is_ref { + self.unify(a_raw, b, ForceLeakCheck::No) + } else if mt_a.mutbl != mutbl_b { + self.unify(a_raw, b, ForceLeakCheck::No) + } else { + self.unify(a_raw, b, ForceLeakCheck::No) + } + } +} + +struct TypeGuidance<'infcx, 'tcx> { + fcx: &'infcx FnCtxt<'infcx, 'tcx>, +} + +impl<'tcx> TypeRelation> for TypeGuidance<'_, 'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.fcx.tcx + } + + fn relate_ty_args( + &mut self, + a_ty: Ty<'tcx>, + _: Ty<'tcx>, + _: DefId, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + _: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> { + relate::relate_args_invariantly(self, a_args, b_args)?; + Ok(a_ty) + } + + fn relate_with_variance>>( + &mut self, + _: ty::Variance, + _: ty::VarianceDiagInfo>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + self.relate(a, b) + } + + #[instrument(skip(self), level = "trace")] + fn regions( + &mut self, + a: ty::Region<'tcx>, + _b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + Ok(a) + } + + #[instrument(skip(self), level = "trace")] + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + if a == b { + return Ok(a); + } + + match (a.kind(), b.kind()) { + (_, ty::Infer(_)) => Ok(a), + (ty::Infer(ty::InferTy::TyVar(a_vid)), _) => { + tracing::debug!(?b); + let a_gen = self + .fcx + .generalize( + rustc_span::DUMMY_SP, + rustc_infer::infer::StructurallyRelateAliases::Yes, + *a_vid, + ty::Variance::Invariant, + b, + )? + .value_may_be_infer; + tracing::debug!(?a_gen); + if a_gen.is_ty_var() { + Ok(a) + } else { + self.fcx.inner.borrow_mut().type_variables().instantiate(*a_vid, a_gen); + relate::structurally_relate_tys(self, a_gen, b) + } + } + (ty::Infer(_), _) => Ok(b), + + _ => relate::structurally_relate_tys(self, a, b), + } + } + + #[instrument(skip(self), level = "trace")] + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + if a == b { + return Ok(a); + } + + match (a.kind(), b.kind()) { + (_, ty::ConstKind::Infer(_)) => Ok(a), + (ty::ConstKind::Infer(_), _) => Ok(b), + _ => relate::structurally_relate_consts(self, a, b), + } + } + + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate>, + { + Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) + } +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 5b40531f94627..5660b099f5bd0 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -23,6 +23,7 @@ use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal}; use rustc_hir_analysis::NoVariantNamed; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin}; +use rustc_infer::traits::ObligationCause; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -1242,7 +1243,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.diverges.set(cond_diverges); } - let result_ty = coerce.complete(self); + let coerce_never = true; + tracing::debug!("calling complete in check_expr_if"); + let expected = expected.coercion_target_type(self, sp); + let result_ty = coerce.complete(self, &self.misc(sp), expected, coerce_never); if let Err(guar) = cond_ty.error_reported() { Ty::new_error(self.tcx, guar) } else { @@ -1463,7 +1467,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if ctxt.coerce.is_none() && !ctxt.may_break { self.dcx().span_bug(body.span, "no coercion, but loop may not break"); } - ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| self.tcx.types.unit) + let cause = ObligationCause::dummy(); + //let coerce_never = self.tcx.expr_guaranteed_to_constitute_read_for_never(expr); + let coerce_never = true; + tracing::debug!("calling complete in check_expr_loop"); + ctxt.coerce + .map(|c| { + c.complete( + self, + &cause, + expected.coercion_target_type(self, body.span), + coerce_never, + ) + }) + .unwrap_or_else(|| self.tcx.types.unit) } /// Checks a method call. @@ -1653,6 +1670,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + #[tracing::instrument(skip(self, args, expr), ret)] fn check_expr_array( &self, args: &'tcx [hir::Expr<'tcx>], @@ -1686,11 +1704,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // While that fixes nested coercion, it will break [some // code like this](https://github.com/rust-lang/rust/pull/140283#issuecomment-2958776528). // If we find a way to support recursive tuple coercion, this break can be avoided. + // + // HACK: *Ideally* we can just use `coerce.merged_ty()`, but the + // following fails: + // ```rust + // let mounts: &[fn(&()) -> &()] = &[ + // |p| p, + // |p| p, + // ]; + // ``` + // This *essentially* comes down to `coerce.merged_ty()` being + // `for<'a> fn(&'a ()) -> &'a ()` on the first arm, but a closure + // with that sig on the second. Then, `check_expr_closure` gets + // that and `deduce_closure_signature` doesn't handle a closure's + // signature, leading to a non-higher ranked fn ptr. + // Oh, and fixing that is just...bad. let e_ty = self.check_expr_with_hint(e, coerce_to); let cause = self.misc(e.span); coerce.coerce(self, &cause, e, e_ty); } - coerce.complete(self) + tracing::debug!("calling complete in check_expr_array"); + coerce.complete(self, &ObligationCause::dummy(), coerce_to, true) } else { self.next_ty_var(expr.span) }; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index e8d5ff017cf8c..6ea2bb0d95c47 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -21,7 +21,7 @@ use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryRespons use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, + Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, }; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, @@ -135,6 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!(?t); return t; } + tracing::debug!(?t); // If not, try resolving pending obligations as much as // possible. This can help substantially when there are @@ -284,7 +285,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self, expr), level = "debug")] pub(crate) fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec>) { - debug!("expr = {:#?}", expr); + debug!(expr = ?expr.hir_id); if adj.is_empty() { return; @@ -370,6 +371,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // A reborrow has no effect before a dereference, so we can safely replace adjustments. *entry.get_mut() = adj; } + ( + &mut [ + Adjustment { + kind: Adjust::Pointer(PointerCoercion::ClosureFnPointer(a_safety)), + target: _a_ty, + }, + ], + &[ + Adjustment { + kind: Adjust::Pointer(PointerCoercion::ClosureFnPointer(b_safety)), + target: _b_ty, + }, + ], + ) if a_safety == b_safety => { + // HACK: See `std::sys::thread::unix::cgroups::quota_v1` for example of what breaks. + // In brief, because we have multiple paths that can re-adjust previously coerced exprs, + // these should all be calling `set_adjustments`, but `try_find_coercion_lub` doesn't + // for fn ptr reification. This started coming up after adding a `coerce.complete()` call + // in `check_expr_array` to maintain backwards compatibility, not really sure if this can + // come up elsewhere. + + // TODO: should probably be comparing `a_ty` and `b_ty`, but I'm *assuming* that this is + // "just valid". Unfortunately, a strict equality doesn't work because of potential inference in + // between. + *entry.get_mut() = adj; + } _ => { // FIXME: currently we never try to compose autoderefs @@ -398,6 +425,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + #[instrument(skip(self, expr), level = "debug")] + pub(crate) fn set_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec>) { + debug!(expr = ?expr.hir_id); + + if adj.is_empty() { + return; + } + + let mut expr_ty = self.typeck_results.borrow().expr_ty_adjusted(expr); + + for a in &adj { + match a.kind { + Adjust::NeverToAny => { + if a.target.is_ty_var() { + self.diverging_type_vars.borrow_mut().insert(a.target); + debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target); + } + } + Adjust::Deref(DerefAdjustKind::Overloaded(overloaded_deref)) => { + self.enforce_context_effects( + None, + expr.span, + overloaded_deref.method_call(self.tcx), + self.tcx.mk_args(&[expr_ty.into()]), + ); + } + Adjust::Deref(DerefAdjustKind::Builtin) => { + // FIXME(const_trait_impl): We *could* enforce `&T: [const] Deref` here. + } + Adjust::Pointer(_pointer_coercion) => { + // FIXME(const_trait_impl): We should probably enforce these. + } + Adjust::ReborrowPin(_mutability) => { + // FIXME(const_trait_impl): We could enforce these; they correspond to + // `&mut T: DerefMut` tho, so it's kinda moot. + } + Adjust::Borrow(_) => { + // No effects to enforce here. + } + } + + expr_ty = a.target; + } + + let autoborrow_mut = adj.iter().any(|adj| { + matches!( + adj, + &Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. })), + .. + } + ) + }); + + self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adj); + + // If there is an mutable auto-borrow, it is equivalent to `&mut `. + // In this case implicit use of `Deref` and `Index` within `` should + // instead be `DerefMut` and `IndexMut`, so fix those up. + if autoborrow_mut { + self.convert_place_derefs_to_mutable(expr); + } + } + /// Instantiates and normalizes the bounds for a given item pub(crate) fn instantiate_bounds( &self, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 54d8306936dd6..420a5f2d48390 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1170,7 +1170,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.diverges.set(prev_diverges); } - let ty = ctxt.coerce.unwrap().complete(self); + let cause = self.cause( + blk.span, + ObligationCauseCode::BlockTailExpression(blk.hir_id, hir::MatchSource::Normal), + ); + let coerce_never = true; + tracing::debug!("calling complete in check_expr_block"); + let ty = ctxt.coerce.unwrap().complete(self, &cause, coerce_to_ty, coerce_never); self.write_ty(blk.hir_id, ty); diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 8692720529d50..738def4b688fa 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -16,6 +16,7 @@ pub mod cast; mod check; mod closure; mod coercion; +mod coercion_guidance; mod demand; mod diverges; mod errors; diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index b573065362601..54df4557b1eca 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -195,7 +195,7 @@ impl<'tcx> InferCtxtInner<'tcx> { } #[inline] - fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'tcx> { + pub fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'tcx> { self.type_variable_storage.with_log(&mut self.undo_log) } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 69c090b662e54..988586cfddddc 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -242,7 +242,7 @@ impl<'tcx> InferCtxt<'tcx> { /// Attempts to generalize `source_term` for the type variable `target_vid`. /// This checks for cycles -- that is, whether `source_term` references `target_vid`. - fn generalize> + Relate>>( + pub fn generalize> + Relate>>( &self, span: Span, structurally_relate_aliases: StructurallyRelateAliases, @@ -739,7 +739,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { /// not only the generalized type, but also a bool flag /// indicating whether further WF checks are needed. #[derive(Debug)] -struct Generalization { +pub struct Generalization { /// When generalizing `::Assoc` or /// `::Assoc>>::Assoc` /// for `?0` generalization returns an inference diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 9b928cc5cc8c2..a85f200420f19 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -84,7 +84,7 @@ pub(crate) struct TypeVariableStorage<'tcx> { sub_unification_table: ut::UnificationTableStorage, } -pub(crate) struct TypeVariableTable<'a, 'tcx> { +pub struct TypeVariableTable<'a, 'tcx> { storage: &'a mut TypeVariableStorage<'tcx>, undo_log: &'a mut InferCtxtUndoLogs<'tcx>, @@ -160,6 +160,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// Records that `a == b`. /// /// Precondition: neither `a` nor `b` are known. + #[tracing::instrument(skip(self))] pub(crate) fn equate(&mut self, a: ty::TyVid, b: ty::TyVid) { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); @@ -180,7 +181,8 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// Instantiates `vid` with the type `ty`. /// /// Precondition: `vid` must not have been previously instantiated. - pub(crate) fn instantiate(&mut self, vid: ty::TyVid, ty: Ty<'tcx>) { + #[tracing::instrument(skip(self))] + pub fn instantiate(&mut self, vid: ty::TyVid, ty: Ty<'tcx>) { let vid = self.root_var(vid); debug_assert!(!ty.is_ty_var(), "instantiating ty var with var: {vid:?} {ty:?}"); debug_assert!(self.probe(vid).is_unknown()); diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 7b61a653ae31e..d09e4c9927bd0 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -185,6 +185,7 @@ where loop { let mut any_changed = false; for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) { + tracing::debug!(?obligation); if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) { self.obligations.on_fulfillment_overflow(infcx); // Only return true errors that we have accumulated while processing. diff --git a/min-tracing b/min-tracing new file mode 100755 index 0000000000000..634ed5e908335 Binary files /dev/null and b/min-tracing differ diff --git a/tests/ui/coercion/coerce-expect-unsized-min.rs b/tests/ui/coercion/coerce-expect-unsized-min.rs new file mode 100644 index 0000000000000..33e18ccb93740 --- /dev/null +++ b/tests/ui/coercion/coerce-expect-unsized-min.rs @@ -0,0 +1,11 @@ +//@ run-pass + +pub fn main() { + /* + let _: Vec _>> = vec![ + Box::new(|x| (x as u8)), + Box::new(|x| (x as i16 as u8)), + ]; + */ + let _: &[Box _>] = &[Box::new(|x| x as u8), Box::new(|x| x as i16 as u8)]; +} diff --git a/tests/ui/coercion/coerce-unify-min.rs b/tests/ui/coercion/coerce-unify-min.rs new file mode 100644 index 0000000000000..affe0cc01ec02 --- /dev/null +++ b/tests/ui/coercion/coerce-unify-min.rs @@ -0,0 +1,8 @@ +//@ run-pass + +fn foo() {} +fn bar() {} + +pub fn main() { + let _ = [bar, foo]; +} diff --git a/tests/ui/coercion/expected-guidance.rs b/tests/ui/coercion/expected-guidance.rs new file mode 100644 index 0000000000000..e9577c58ba4f3 --- /dev/null +++ b/tests/ui/coercion/expected-guidance.rs @@ -0,0 +1,16 @@ +//@ check-pass + +struct Foo; +impl Foo { + fn and(self, _other: Foo) -> Foo { Foo } +} + +fn example() { + let mut interest = None; + interest = match interest.take() { // expected: Option + None => Some(Foo), + Some(that) => Some(that.and(Foo)), + } +} + +fn main() {} diff --git a/tests/ui/coercion/multistep-inkanalyzer-regression.rs b/tests/ui/coercion/multistep-inkanalyzer-regression.rs new file mode 100644 index 0000000000000..6abe18ab0f0f0 --- /dev/null +++ b/tests/ui/coercion/multistep-inkanalyzer-regression.rs @@ -0,0 +1,13 @@ +//@ check-pass + +// In the actual code from inkanalyzer, the code is more complex and non-recursive +#[allow(unconditional_recursion)] +fn ink_attrs_closest_descendants() -> impl Iterator { + if true { + Box::new(ink_attrs_closest_descendants()) as Box> + } else { + Box::new(ink_attrs_closest_descendants()) + } +} + +fn main() {} diff --git a/tests/ui/coercion/multistep-typetag-regression.rs b/tests/ui/coercion/multistep-typetag-regression.rs new file mode 100644 index 0000000000000..1029a2aa0f005 --- /dev/null +++ b/tests/ui/coercion/multistep-typetag-regression.rs @@ -0,0 +1,12 @@ +//@ check-pass + +trait Sendable: Send {} + +fn deserialize_not_send() { + let _: Box = match true { + true => loop {}, + false => (loop {}) as Box, + }; +} + +fn main() {} diff --git a/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region-2-min.rs b/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region-2-min.rs new file mode 100644 index 0000000000000..7765637df16c8 --- /dev/null +++ b/tests/ui/impl-trait/defining-use-uncaptured-non-universal-region-2-min.rs @@ -0,0 +1,21 @@ +trait Trait {} +impl Trait for () {} +fn build( + mut commands: impl Trait, +) -> Option { + // Different combinations result in the following equalities (with different orderings) + // () == build::::opaque>::opaque == build::::opaque> + match () { + //_ => Some(()), + _ => build(commands), + _ => { + let further_commands = match build(commands) { + Some(c) => c, + None => return None, + }; + build(further_commands) + } + } +} + +fn main() {}