diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 3a5eeb5381bec..343b2096c5ae7 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -1,5 +1,5 @@ use crate::check::coercion::CoerceMany; -use crate::check::{Diverges, Expectation, FnCtxt, Needs}; +use crate::check::{Diverges, Expectation, FnCtxt, Needs, TypeAscriptionCtxt}; use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::Obligation; @@ -41,7 +41,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 2. By expecting `bool` for `expr` we get nice diagnostics for e.g. `if x = y { .. }`. // // FIXME(60707): Consider removing hack with principled solution. - self.check_expr_has_type_or_error(scrut, self.tcx.types.bool, |_| {}) + self.check_expr_has_type_or_error( + scrut, + self.tcx.types.bool, + |_| {}, + orig_expected.get_coercion_ctxt(), + ) } else { self.demand_scrutinee_type(arms, scrut) }; @@ -82,7 +87,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // us to give better error messages (pointing to a usually better // arm for inconsistent arms or to the whole match when a `()` type // is required). - Expectation::ExpectHasType(ety) if ety != self.tcx.mk_unit() => ety, + Expectation::ExpectHasType(ety, _) if ety != self.tcx.mk_unit() => ety, _ => self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span: expr.span, @@ -97,9 +102,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(g) = &arm.guard { self.diverges.set(Diverges::Maybe); match g { - hir::Guard::If(e) => { - self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {}) - } + hir::Guard::If(e) => self.check_expr_has_type_or_error( + e, + tcx.types.bool, + |_| {}, + expected.get_coercion_ctxt(), + ), }; } @@ -124,7 +132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { orig_expected, self.ret_coercion_impl_trait.map(|ty| (self.body_id.owner, ty)), ) { - (Expectation::ExpectHasType(expected), Some((id, ty))) + (Expectation::ExpectHasType(expected, _), Some((id, ty))) if self.in_tail_expr && self.can_coerce(arm_ty, expected) => { let impl_trait_ret_ty = self.infcx.instantiate_opaque_types( @@ -525,7 +533,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: TypeVariableOriginKind::TypeInference, span: scrut.span, }); - self.check_expr_has_type_or_error(scrut, scrut_ty, |_| {}); + self.check_expr_has_type_or_error(scrut, scrut_ty, |_| {}, TypeAscriptionCtxt::Normal); scrut_ty } } diff --git a/compiler/rustc_typeck/src/check/expectation.rs b/compiler/rustc_typeck/src/check/expectation.rs index 5a5fc893d6588..c76b0e8571c92 100644 --- a/compiler/rustc_typeck/src/check/expectation.rs +++ b/compiler/rustc_typeck/src/check/expectation.rs @@ -5,22 +5,44 @@ use rustc_span::{self, Span}; use super::Expectation::*; use super::FnCtxt; +// We permit coercion of type ascriptions in coercion sites and sub-expressions +// that originate from coercion sites. When we encounter a +// coercion site we propagate this expectation of coercions of type ascriptions +// down into sub-expressions by providing the `Expectation` with a +// TypeAscriptionCtxt::Coercion. Whenever we encounter an expression of +// ExprKind::Type in a sub-expression and TypeAscriptionCtxt is set, we coerce +// the type ascription +#[derive(Copy, Clone, Debug)] +pub enum TypeAscriptionCtxt { + Coercion, + Normal, +} + +impl TypeAscriptionCtxt { + fn is_coercion_site(self) -> bool { + match self { + TypeAscriptionCtxt::Coercion => true, + TypeAscriptionCtxt::Normal => false, + } + } +} + /// When type-checking an expression, we propagate downward /// whatever type hint we are able in the form of an `Expectation`. #[derive(Copy, Clone, Debug)] pub enum Expectation<'tcx> { /// We know nothing about what type this expression should have. - NoExpectation, + NoExpectation(TypeAscriptionCtxt), /// This expression should have the type given (or some subtype). - ExpectHasType(Ty<'tcx>), + ExpectHasType(Ty<'tcx>, TypeAscriptionCtxt), /// This expression will be cast to the `Ty`. - ExpectCastableToType(Ty<'tcx>), + ExpectCastableToType(Ty<'tcx>, TypeAscriptionCtxt), /// This rvalue expression will be wrapped in `&` or `Box` and coerced /// to `&Ty` or `Box`, respectively. `Ty` is `[A]` or `Trait`. - ExpectRvalueLikeUnsized(Ty<'tcx>), + ExpectRvalueLikeUnsized(Ty<'tcx>, TypeAscriptionCtxt), } impl<'a, 'tcx> Expectation<'tcx> { @@ -42,12 +64,12 @@ impl<'a, 'tcx> Expectation<'tcx> { // 'else' branch. pub(super) fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { match *self { - ExpectHasType(ety) => { + ExpectHasType(ety, ctxt) => { let ety = fcx.shallow_resolve(ety); - if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation } + if !ety.is_ty_var() { ExpectHasType(ety, ctxt) } else { NoExpectation(ctxt) } } - ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety), - _ => NoExpectation, + ExpectRvalueLikeUnsized(ety, ctxt) => ExpectRvalueLikeUnsized(ety, ctxt), + _ => NoExpectation(TypeAscriptionCtxt::Normal), } } @@ -70,10 +92,14 @@ impl<'a, 'tcx> Expectation<'tcx> { /// which still is useful, because it informs integer literals and the like. /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 /// for examples of where this comes up,. - pub(super) fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> { + pub(super) fn rvalue_hint( + fcx: &FnCtxt<'a, 'tcx>, + ty: Ty<'tcx>, + ctxt: TypeAscriptionCtxt, + ) -> Expectation<'tcx> { match fcx.tcx.struct_tail_without_normalization(ty).kind() { - ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty), - _ => ExpectHasType(ty), + ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty, ctxt), + _ => ExpectHasType(ty, ctxt), } } @@ -82,17 +108,23 @@ impl<'a, 'tcx> Expectation<'tcx> { // no constraints yet present), just returns `None`. fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { match self { - NoExpectation => NoExpectation, - ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(t)), - ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(t)), - ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(t)), + NoExpectation(ctxt) => NoExpectation(ctxt), + ExpectCastableToType(t, ctxt) => { + ExpectCastableToType(fcx.resolve_vars_if_possible(t), ctxt) + } + ExpectHasType(t, ctxt) => ExpectHasType(fcx.resolve_vars_if_possible(t), ctxt), + ExpectRvalueLikeUnsized(t, ctxt) => { + ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(t), ctxt) + } } } pub(super) fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option> { match self.resolve(fcx) { - NoExpectation => None, - ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty), + NoExpectation(_) => None, + ExpectCastableToType(ty, _) | ExpectHasType(ty, _) | ExpectRvalueLikeUnsized(ty, _) => { + Some(ty) + } } } @@ -102,8 +134,8 @@ impl<'a, 'tcx> Expectation<'tcx> { /// such a constraint, if it exists. pub(super) fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option> { match self.resolve(fcx) { - ExpectHasType(ty) => Some(ty), - NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None, + ExpectHasType(ty, _) => Some(ty), + NoExpectation(_) | ExpectCastableToType(_, _) | ExpectRvalueLikeUnsized(_, _) => None, } } @@ -114,4 +146,17 @@ impl<'a, 'tcx> Expectation<'tcx> { fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }) }) } + + pub(super) fn get_coercion_ctxt(self) -> TypeAscriptionCtxt { + match self { + ExpectHasType(_, ctxt) => ctxt, + ExpectCastableToType(_, ctxt) => ctxt, + ExpectRvalueLikeUnsized(_, ctxt) => ctxt, + NoExpectation(ctxt) => ctxt, + } + } + + pub(super) fn coerce_type_ascriptions(self) -> bool { + self.get_coercion_ctxt().is_coercion_site() + } } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 26962d2222d32..9865953709290 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -14,6 +14,7 @@ use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExp use crate::check::FnCtxt; use crate::check::Needs; use crate::check::TupleArgumentsFlag::DontTupleArguments; +use crate::check::TypeAscriptionCtxt; use crate::errors::{ FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, YieldExprOutsideOfGenerator, @@ -57,8 +58,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, extend_err: impl Fn(&mut DiagnosticBuilder<'_>), + type_ascr_ctxt: TypeAscriptionCtxt, ) -> Ty<'tcx> { - self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err) + self.check_expr_meets_expectation_or_error( + expr, + ExpectHasType(expected, type_ascr_ctxt), + extend_err, + ) } fn check_expr_meets_expectation_or_error( @@ -101,12 +107,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(super) fn check_expr_coercable_to_type( &self, expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, + expected_ty: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + ctxt: TypeAscriptionCtxt, ) -> Ty<'tcx> { - let ty = self.check_expr_with_hint(expr, expected); + let ty = self.check_expr_with_expectation(expr, ExpectHasType(expected_ty, ctxt)); // checks don't need two phase - self.demand_coerce(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No) + self.demand_coerce(expr, ty, expected_ty, expected_ty_expr, AllowTwoPhase::No) } pub(super) fn check_expr_with_hint( @@ -114,7 +121,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, ) -> Ty<'tcx> { - self.check_expr_with_expectation(expr, ExpectHasType(expected)) + self.check_expr_with_expectation(expr, ExpectHasType(expected, TypeAscriptionCtxt::Normal)) } fn check_expr_with_expectation_and_needs( @@ -135,7 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { - self.check_expr_with_expectation(expr, NoExpectation) + self.check_expr_with_expectation(expr, NoExpectation(TypeAscriptionCtxt::Normal)) } pub(super) fn check_expr_with_needs( @@ -143,7 +150,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, needs: Needs, ) -> Ty<'tcx> { - self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs) + self.check_expr_with_expectation_and_needs( + expr, + NoExpectation(TypeAscriptionCtxt::Normal), + needs, + ) } /// Invariant: @@ -281,8 +292,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Cast(ref e, ref t) => self.check_expr_cast(e, t, expr), ExprKind::Type(ref e, ref t) => { let ty = self.to_ty_saving_user_provided_ty(&t); - self.check_expr_eq_type(&e, ty); - ty + + // coerce type ascriptions if we're inside some coercion site + if expected.coerce_type_ascriptions() { + self.check_expr_coercable_to_type(e, ty, None, TypeAscriptionCtxt::Coercion) + } else { + self.check_expr_eq_type(&e, ty); + ty + } } ExprKind::DropTemps(ref e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(ref args) => self.check_expr_array(args, expected, expr), @@ -302,10 +319,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn check_expr_box(&self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>) -> Ty<'tcx> { - let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind() { - ty::Adt(def, _) if def.is_box() => Expectation::rvalue_hint(self, ty.boxed_ty()), - _ => NoExpectation, - }); + let type_ascr_ctxt = expected.get_coercion_ctxt(); + let expected_inner = + expected.to_option(self).map_or(NoExpectation(type_ascr_ctxt), |ty| match ty.kind() { + ty::Adt(def, _) if def.is_box() => { + Expectation::rvalue_hint(self, ty.boxed_ty(), type_ascr_ctxt) + } + _ => NoExpectation(type_ascr_ctxt), + }); let referent_ty = self.check_expr_with_expectation(expr, expected_inner); self.tcx.mk_box(referent_ty) } @@ -320,7 +341,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let expected_inner = match unop { hir::UnOp::UnNot | hir::UnOp::UnNeg => expected, - hir::UnOp::UnDeref => NoExpectation, + hir::UnOp::UnDeref => NoExpectation(expected.get_coercion_ctxt()), }; let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner); @@ -376,19 +397,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { + let type_ascr_ctxt = expected.get_coercion_ctxt(); + let hint = expected.only_has_type(self).map_or(NoExpectation(type_ascr_ctxt), |ty| { match ty.kind() { ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { if oprnd.is_syntactic_place_expr() { // Places may legitimately have unsized types. // For example, dereferences of a fat pointer and // the last field of a struct can be unsized. - ExpectHasType(ty) + ExpectHasType(ty, type_ascr_ctxt) } else { - Expectation::rvalue_hint(self, ty) + Expectation::rvalue_hint(self, ty, type_ascr_ctxt) } } - _ => NoExpectation, + _ => NoExpectation(type_ascr_ctxt), } }); let ty = @@ -709,7 +731,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let ret_ty = ret_coercion.borrow().expected_ty(); - let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty.clone()); + // This is a coercion site, so we allow coercion of type ascriptions in return_expr + let expected = ExpectHasType(ret_ty.clone(), TypeAscriptionCtxt::Coercion); + let return_expr_ty = self.check_expr_with_expectation(return_expr, expected); ret_coercion.borrow_mut().coerce( self, &self.cause(return_expr.span, ObligationCauseCode::ReturnValue(return_expr.hir_id)), @@ -805,7 +829,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_lhs_assignable(lhs, "E0070", span); let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); - let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs)); + let rhs_ty = + self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs), TypeAscriptionCtxt::Normal); self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); @@ -964,7 +989,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // if appropriate. let t_cast = self.to_ty_saving_user_provided_ty(t); let t_cast = self.resolve_vars_if_possible(t_cast); - let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast)); + let t_expr = self.check_expr_with_expectation( + e, + ExpectCastableToType(t_cast, TypeAscriptionCtxt::Normal), + ); let t_cast = self.resolve_vars_if_possible(t_cast); // Eagerly check for some obvious errors. @@ -1005,7 +1033,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); assert_eq!(self.diverges.get(), Diverges::Maybe); for e in args { - let e_ty = self.check_expr_with_hint(e, coerce_to); + let e_ty = self.check_expr_with_expectation( + e, + ExpectHasType(coerce_to, expected.get_coercion_ctxt()), + ); let cause = self.misc(e.span); coerce.coerce(self, &cause, e, e_ty); } @@ -1030,16 +1061,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let count = self.to_const(count); let uty = match expected { - ExpectHasType(uty) => match *uty.kind() { + ExpectHasType(uty, _) => match *uty.kind() { ty::Array(ty, _) | ty::Slice(ty) => Some(ty), _ => None, }, _ => None, }; + let type_ascr_ctxt = expected.get_coercion_ctxt(); let (element_ty, t) = match uty { Some(uty) => { - self.check_expr_coercable_to_type(&element, uty, None); + self.check_expr_coercable_to_type(&element, uty, None, type_ascr_ctxt); (uty, uty) } None => { @@ -1047,7 +1079,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: TypeVariableOriginKind::MiscVariable, span: element.span, }); - let element_ty = self.check_expr_has_type_or_error(&element, ty, |_| {}); + let element_ty = + self.check_expr_has_type_or_error(&element, ty, |_| {}, type_ascr_ctxt); (element_ty, ty) } }; @@ -1073,13 +1106,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }); + let type_ascr_ctxt = expected.get_coercion_ctxt(); let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| match flds { Some(ref fs) if i < fs.len() => { let ety = fs[i].expect_ty(); - self.check_expr_coercable_to_type(&e, ety, None); + self.check_expr_coercable_to_type(&e, ety, None, type_ascr_ctxt); ety } - _ => self.check_expr_with_expectation(&e, NoExpectation), + _ => self.check_expr_with_expectation(&e, NoExpectation(type_ascr_ctxt)), }); let tuple = self.tcx.mk_tup(elt_ts_iter); if tuple.references_error() { @@ -1129,7 +1163,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the fields with the base_expr. This could cause us to hit errors later // when certain fields are assumed to exist that in fact do not. if !error_happened { - self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {}); + self.check_expr_has_type_or_error( + base_expr, + adt_ty, + |_| {}, + TypeAscriptionCtxt::Normal, + ); match adt_ty.kind() { ty::Adt(adt, substs) if adt.is_struct() => { let fru_field_types = adt @@ -1229,7 +1268,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Make sure to give a type to the field even if there's // an error, so we can continue type-checking. - self.check_expr_coercable_to_type(&field.expr, field_type, None); + self.check_expr_coercable_to_type( + &field.expr, + field_type, + None, + TypeAscriptionCtxt::Coercion, + ); } // Make sure the programmer specified correct number of fields. @@ -1875,7 +1919,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { match self.resume_yield_tys { Some((resume_ty, yield_ty)) => { - self.check_expr_coercable_to_type(&value, yield_ty, None); + self.check_expr_coercable_to_type( + &value, + yield_ty, + None, + TypeAscriptionCtxt::Normal, + ); resume_ty } @@ -1884,7 +1933,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // information. Hence, we check the source of the yield expression here and check its // value's type against `()` (this check should always hold). None if src.is_await() => { - self.check_expr_coercable_to_type(&value, self.tcx.mk_unit(), None); + self.check_expr_coercable_to_type( + &value, + self.tcx.mk_unit(), + None, + TypeAscriptionCtxt::Normal, + ); self.tcx.mk_unit() } _ => { diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 333bda00dbe81..8cc6f27e912a4 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -3,6 +3,7 @@ use crate::check::coercion::CoerceMany; use crate::check::method::MethodCallee; use crate::check::Expectation::*; use crate::check::TupleArgumentsFlag::*; +use crate::check::TypeAscriptionCtxt; use crate::check::{ potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, TupleArgumentsFlag, @@ -339,7 +340,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // The special-cased logic below has three functions: // 1. Provide as good of an expected type as possible. - let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]); + let expected = Expectation::rvalue_hint( + self, + expected_arg_tys[i], + TypeAscriptionCtxt::Coercion, + ); let checked_ty = self.check_expr_with_expectation(&arg, expected); @@ -509,7 +514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype(init.span, local_ty, init_ty); init_ty } else { - self.check_expr_coercable_to_type(init, local_ty, None) + self.check_expr_coercable_to_type(init, local_ty, None, TypeAscriptionCtxt::Coercion) } } @@ -559,9 +564,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::StmtKind::Item(_) => {} hir::StmtKind::Expr(ref expr) => { // Check with expected type of `()`. - self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { - self.suggest_semicolon_at_end(expr.span, err); - }); + self.check_expr_has_type_or_error( + &expr, + self.tcx.mk_unit(), + |err| { + self.suggest_semicolon_at_end(expr.span, err); + }, + TypeAscriptionCtxt::Normal, + ); } hir::StmtKind::Semi(ref expr) => { self.check_expr(&expr); @@ -575,7 +585,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { let unit = self.tcx.mk_unit(); - let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); + let ty = + self.check_block_with_expected(blk, ExpectHasType(unit, TypeAscriptionCtxt::Normal)); // if the block produces a `!` value, that can always be // (effectively) coerced to unit. diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index d27a68ccf1bda..ef07290c167da 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -96,6 +96,7 @@ use check::{ pub use check::{check_item_type, check_wf_new}; pub use diverges::Diverges; pub use expectation::Expectation; +pub use expectation::TypeAscriptionCtxt; pub use fn_ctxt::*; pub use inherited::{Inherited, InheritedBuilder}; @@ -555,7 +556,12 @@ fn typeck_with_fallback<'tcx>( // Gather locals in statics (because of block expressions). GatherLocalsVisitor::new(&fcx, id).visit_body(body); - fcx.check_expr_coercable_to_type(&body.value, revealed_ty, None); + fcx.check_expr_coercable_to_type( + &body.value, + revealed_ty, + None, + TypeAscriptionCtxt::Normal, + ); fcx.write_ty(id, revealed_ty); diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 854bc70108f69..8bb8fd37c385f 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -2,6 +2,7 @@ use super::method::MethodCallee; use super::FnCtxt; +use super::TypeAscriptionCtxt; use rustc_ast as ast; use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; @@ -64,9 +65,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match BinOpCategory::from(op) { BinOpCategory::Shortcircuit => { // && and || are a simple case. - self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None); + self.check_expr_coercable_to_type( + lhs_expr, + tcx.types.bool, + None, + TypeAscriptionCtxt::Normal, + ); let lhs_diverges = self.diverges.get(); - self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None); + self.check_expr_coercable_to_type( + rhs_expr, + tcx.types.bool, + None, + TypeAscriptionCtxt::Normal, + ); // Depending on the LHS' value, the RHS can never execute. self.diverges.set(lhs_diverges); @@ -203,7 +214,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let result = self.lookup_op_method(lhs_ty, &[rhs_ty_var], Op::Binary(op, is_assign)); // see `NB` above - let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr)); + let rhs_ty = self.check_expr_coercable_to_type( + rhs_expr, + rhs_ty_var, + Some(lhs_expr), + TypeAscriptionCtxt::Normal, + ); let rhs_ty = self.resolve_vars_with_obligations(rhs_ty); let return_ty = match result { diff --git a/src/test/ui/coercion/coerce-expect-unsized-ascribed.rs b/src/test/ui/coercion/coerce-expect-unsized-ascribed.rs index c139e823c2aef..92e8cada45914 100644 --- a/src/test/ui/coercion/coerce-expect-unsized-ascribed.rs +++ b/src/test/ui/coercion/coerce-expect-unsized-ascribed.rs @@ -1,29 +1,29 @@ // A version of coerce-expect-unsized that uses type ascription. // Doesn't work so far, but supposed to work eventually +// build-pass + #![feature(box_syntax, type_ascription)] use std::fmt::Debug; pub fn main() { - let _ = box { [1, 2, 3] }: Box<[i32]>; //~ ERROR mismatched types - let _ = box if true { [1, 2, 3] } else { [1, 3, 4] }: Box<[i32]>; //~ ERROR mismatched types + let _ = box { [1, 2, 3] }: Box<[i32]>; + let _ = box if true { [1, 2, 3] } else { [1, 3, 4] }: Box<[i32]>; let _ = box match true { true => [1, 2, 3], false => [1, 3, 4] }: Box<[i32]>; - //~^ ERROR mismatched types - let _ = box { |x| (x as u8) }: Box _>; //~ ERROR mismatched types - let _ = box if true { false } else { true }: Box; //~ ERROR mismatched types - let _ = box match true { true => 'a', false => 'b' }: Box; //~ ERROR mismatched types + let _ = box { |x| (x as u8) }: Box _>; + let _ = box if true { false } else { true }: Box; + let _ = box match true { true => 'a', false => 'b' }: Box; - let _ = &{ [1, 2, 3] }: &[i32]; //~ ERROR mismatched types - let _ = &if true { [1, 2, 3] } else { [1, 3, 4] }: &[i32]; //~ ERROR mismatched types + let _ = &{ [1, 2, 3] }: &[i32]; + let _ = &if true { [1, 2, 3] } else { [1, 3, 4] }: &[i32]; let _ = &match true { true => [1, 2, 3], false => [1, 3, 4] }: &[i32]; - //~^ ERROR mismatched types - let _ = &{ |x| (x as u8) }: &dyn Fn(i32) -> _; //~ ERROR mismatched types - let _ = &if true { false } else { true }: &dyn Debug; //~ ERROR mismatched types - let _ = &match true { true => 'a', false => 'b' }: &dyn Debug; //~ ERROR mismatched types + let _ = &{ |x| (x as u8) }: &dyn Fn(i32) -> _; + let _ = &if true { false } else { true }: &dyn Debug; + let _ = &match true { true => 'a', false => 'b' }: &dyn Debug; - let _ = Box::new([1, 2, 3]): Box<[i32]>; //~ ERROR mismatched types - let _ = Box::new(|x| (x as u8)): Box _>; //~ ERROR mismatched types + let _ = Box::new([1, 2, 3]): Box<[i32]>; + let _ = Box::new(|x| (x as u8)): Box _>; let _ = vec![ Box::new(|x| (x as u8)), diff --git a/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr b/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr deleted file mode 100644 index f0109f22a2bc1..0000000000000 --- a/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr +++ /dev/null @@ -1,129 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:9:13 - | -LL | let _ = box { [1, 2, 3] }: Box<[i32]>; - | ^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]` - | - = note: expected struct `Box<[i32]>` - found struct `Box<[i32; 3]>` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:10:13 - | -LL | let _ = box if true { [1, 2, 3] } else { [1, 3, 4] }: Box<[i32]>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]` - | - = note: expected struct `Box<[i32]>` - found struct `Box<[i32; 3]>` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:11:13 - | -LL | let _ = box match true { true => [1, 2, 3], false => [1, 3, 4] }: Box<[i32]>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]` - | - = note: expected struct `Box<[i32]>` - found struct `Box<[i32; 3]>` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:13:13 - | -LL | let _ = box { |x| (x as u8) }: Box _>; - | ^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure - | - = note: expected struct `Box u8>` - found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:13:19: 13:32]>` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:14:13 - | -LL | let _ = box if true { false } else { true }: Box; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool` - | - = note: expected struct `Box` - found struct `Box` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:15:13 - | -LL | let _ = box match true { true => 'a', false => 'b' }: Box; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char` - | - = note: expected struct `Box` - found struct `Box` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:17:13 - | -LL | let _ = &{ [1, 2, 3] }: &[i32]; - | ^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]` - | - = note: expected reference `&[i32]` - found reference `&[i32; 3]` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:18:13 - | -LL | let _ = &if true { [1, 2, 3] } else { [1, 3, 4] }: &[i32]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]` - | - = note: expected reference `&[i32]` - found reference `&[i32; 3]` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:19:13 - | -LL | let _ = &match true { true => [1, 2, 3], false => [1, 3, 4] }: &[i32]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]` - | - = note: expected reference `&[i32]` - found reference `&[i32; 3]` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:21:13 - | -LL | let _ = &{ |x| (x as u8) }: &dyn Fn(i32) -> _; - | ^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure - | - = note: expected reference `&dyn Fn(i32) -> u8` - found reference `&[closure@$DIR/coerce-expect-unsized-ascribed.rs:21:16: 21:29]` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:22:13 - | -LL | let _ = &if true { false } else { true }: &dyn Debug; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool` - | - = note: expected reference `&dyn Debug` - found reference `&bool` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:23:13 - | -LL | let _ = &match true { true => 'a', false => 'b' }: &dyn Debug; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char` - | - = note: expected reference `&dyn Debug` - found reference `&char` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:25:13 - | -LL | let _ = Box::new([1, 2, 3]): Box<[i32]>; - | ^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]` - | - = note: expected struct `Box<[i32]>` - found struct `Box<[i32; 3]>` - -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:26:13 - | -LL | let _ = Box::new(|x| (x as u8)): Box _>; - | ^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure - | - = note: expected struct `Box u8>` - found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:26:22: 26:35]>` - -error: aborting due to 14 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/coercion/type-ascription/coerce-array-repeat-in-fn-arg.rs b/src/test/ui/coercion/type-ascription/coerce-array-repeat-in-fn-arg.rs new file mode 100644 index 0000000000000..590b903bfcaac --- /dev/null +++ b/src/test/ui/coercion/type-ascription/coerce-array-repeat-in-fn-arg.rs @@ -0,0 +1,10 @@ +#![feature(type_ascription)] + +// build-pass + +fn foo(_arg : [&[u32];3]) {} + +fn main() { + let arr = [4,5,6]; + foo([&arr : &[u32]; 3]); +} diff --git a/src/test/ui/coercion/type-ascription/coerce-enum-variant.rs b/src/test/ui/coercion/type-ascription/coerce-enum-variant.rs new file mode 100644 index 0000000000000..147dcb4796f83 --- /dev/null +++ b/src/test/ui/coercion/type-ascription/coerce-enum-variant.rs @@ -0,0 +1,13 @@ +// build-pass + +#![feature(type_ascription)] + +enum Foo<'a> { + A((u32, &'a [u32])), + B((u32, &'a [u32; 4])), +} + +fn main() { + let arr = [4,5,6]; + let temp = Foo::A((10, &arr : &[u32])); +} diff --git a/src/test/ui/coercion/type-ascription/coerce-nested-tuple-in-let-stmt.rs b/src/test/ui/coercion/type-ascription/coerce-nested-tuple-in-let-stmt.rs new file mode 100644 index 0000000000000..6477e143f1476 --- /dev/null +++ b/src/test/ui/coercion/type-ascription/coerce-nested-tuple-in-let-stmt.rs @@ -0,0 +1,16 @@ +// run-pass + +#![feature(type_ascription)] + +use std::any::type_name; +use std::assert_eq; + +fn type_of(_: T) -> &'static str { + type_name::() +} + +fn main() { + let arr = [5,6,7]; + let tup = (5, (3, (12, &arr : &[u32])), &arr : &[u32]); + assert_eq!(type_of(tup), "(i32, (i32, (i32, &[u32])), &[u32])"); +} diff --git a/src/test/ui/coercion/type-ascription/coerce-return-expr.rs b/src/test/ui/coercion/type-ascription/coerce-return-expr.rs new file mode 100644 index 0000000000000..b9b7c691e14d7 --- /dev/null +++ b/src/test/ui/coercion/type-ascription/coerce-return-expr.rs @@ -0,0 +1,12 @@ +#![feature(type_ascription)] + +fn foo<'a>(arg : (u32, (u32, &'a [u32; 3]))) -> (u32, (u32, &'a [u32])) { + arg : (u32, (u32, &[u32])) + //~^ ERROR: mismatched types +} + +fn main() { + let arr = [4,5,6]; + let tup = (3, (9, &arr)); + let result = foo(tup); +} diff --git a/src/test/ui/coercion/type-ascription/coerce-return-expr.stderr b/src/test/ui/coercion/type-ascription/coerce-return-expr.stderr new file mode 100644 index 0000000000000..0a6d26cfa44a6 --- /dev/null +++ b/src/test/ui/coercion/type-ascription/coerce-return-expr.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/coerce-return-expr.rs:4:3 + | +LL | arg : (u32, (u32, &[u32])) + | ^^^ expected slice `[u32]`, found array `[u32; 3]` + | + = note: expected tuple `(_, (_, &[u32]))` + found tuple `(_, (_, &'a [u32; 3]))` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/coercion/type-ascription/coerce-struct-fields-pass-in-let-stmt.rs b/src/test/ui/coercion/type-ascription/coerce-struct-fields-pass-in-let-stmt.rs new file mode 100644 index 0000000000000..8feccdd513172 --- /dev/null +++ b/src/test/ui/coercion/type-ascription/coerce-struct-fields-pass-in-let-stmt.rs @@ -0,0 +1,29 @@ +// Here we test for coercions in struct fields of nested type ascriptions +// inside a tuple using an unsized coercion and a coercion from &mut -> & + +// run-pass + +#![feature(type_ascription)] + +use std::any::type_name; +use std::assert_eq; + +fn type_of(_: T) -> &'static str { + type_name::() +} + +struct Foo<'a, 'b, T1, T2> { + _a : (T1, (T1, &'a [T1]), &'b T2), +} + +struct Bar { + _b : u32, +} + +fn main() { + let mut bar = Bar {_b : 26}; + let arr = [4,5,6]; + let tup = (9, (3, &arr : &[u32]), &mut bar); + assert_eq!(type_of(tup), "(i32, (i32, &[u32]), &mut coerce_struct_fields_pass_in_let_stmt::Bar)"); + let _ = Foo { _a : (9, (3, &arr : &[u32]), &mut bar) }; +} diff --git a/src/test/ui/coercion/type-ascription/coerce-to-unsized-in-return-expr.rs b/src/test/ui/coercion/type-ascription/coerce-to-unsized-in-return-expr.rs new file mode 100644 index 0000000000000..38350211fda93 --- /dev/null +++ b/src/test/ui/coercion/type-ascription/coerce-to-unsized-in-return-expr.rs @@ -0,0 +1,12 @@ +// build-pass + +#![feature(type_ascription)] + +fn foo<'a>(arg : &'a [u32; 3]) -> &'a [u32] { + arg : &[u32] +} + +fn main() { + let arr = [4,5,6]; + let _ = foo(&arr); +} diff --git a/src/test/ui/coercion/type-ascription/coerce-union-field.rs b/src/test/ui/coercion/type-ascription/coerce-union-field.rs new file mode 100644 index 0000000000000..6383ca99220c9 --- /dev/null +++ b/src/test/ui/coercion/type-ascription/coerce-union-field.rs @@ -0,0 +1,14 @@ +// build-pass + +#![feature(type_ascription)] + +union Foo<'a> { + f1: (&'a u32, (u32, &'a [u32])), + _f2: u32, +} + +fn main() { + let arr = [4,5,6]; + let x = &mut 26; + let _ = Foo { f1: (x : &u32, (5, &arr : &[u32])) }; +} diff --git a/src/test/ui/coercion/type-ascription/unsound-type-ascription-coercion.rs b/src/test/ui/coercion/type-ascription/unsound-type-ascription-coercion.rs new file mode 100644 index 0000000000000..7dde7dce58e87 --- /dev/null +++ b/src/test/ui/coercion/type-ascription/unsound-type-ascription-coercion.rs @@ -0,0 +1,17 @@ +#![feature(type_ascription)] + +fn f() { + let mut x: &'static u32 = &22; + let y = &44; + foo(x, y); +} + +fn foo<'a, 'b>(arg1 : &'a u32, arg2 : &'b u32) { + let p = &mut (arg1 : &'b u32); + //~^ ERROR: lifetime mismatch + *p = arg2; +} + +fn main() { + f(); +} diff --git a/src/test/ui/coercion/type-ascription/unsound-type-ascription-coercion.stderr b/src/test/ui/coercion/type-ascription/unsound-type-ascription-coercion.stderr new file mode 100644 index 0000000000000..445fc6739f419 --- /dev/null +++ b/src/test/ui/coercion/type-ascription/unsound-type-ascription-coercion.stderr @@ -0,0 +1,13 @@ +error[E0623]: lifetime mismatch + --> $DIR/unsound-type-ascription-coercion.rs:10:17 + | +LL | fn foo<'a, 'b>(arg1 : &'a u32, arg2 : &'b u32) { + | ------- ------- + | | + | these two types are declared with different lifetimes... +LL | let p = &mut (arg1 : &'b u32); + | ^^^^ ...but data from `arg1` flows into `arg2` here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0623`.