]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_typeck/src/check/expr.rs
New upstream version 1.51.0+dfsg1
[rustc.git] / compiler / rustc_typeck / src / check / expr.rs
index ec0e039b5d29d99867fa0672f3104a6421f41129..33b1c0bb2c97cb8a50ed6a8a3ef688370d23a5e7 100644 (file)
@@ -10,6 +10,7 @@ use crate::check::method::{probe, MethodError, SelfSource};
 use crate::check::report_unexpected_variant_res;
 use crate::check::BreakableCtxt;
 use crate::check::Diverges;
+use crate::check::DynamicCoerceMany;
 use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation};
 use crate::check::FnCtxt;
 use crate::check::Needs;
@@ -35,17 +36,17 @@ use rustc_infer::infer;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_middle::ty;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
+use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::TypeFoldable;
 use rustc_middle::ty::{AdtKind, Visibility};
+use rustc_span::edition::LATEST_STABLE_EDITION;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_trait_selection::traits::{self, ObligationCauseCode};
 
-use std::fmt::Display;
-
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) {
         let ty = self.check_expr_with_hint(expr, expected);
@@ -187,7 +188,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Warn for non-block expressions with diverging children.
         match expr.kind {
-            ExprKind::Block(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {}
+            ExprKind::Block(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {}
             // If `expr` is a result of desugaring the try block and is an ok-wrapped
             // diverging expression (e.g. it arose from desugaring of `try { return }`),
             // we skip issuing a warning because it is autogenerated code.
@@ -264,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
             ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
-            ExprKind::Loop(ref body, _, source) => {
+            ExprKind::Loop(ref body, _, source, _) => {
                 self.check_expr_loop(body, source, expected, expr)
             }
             ExprKind::Match(ref discrim, ref arms, match_src) => {
@@ -284,6 +285,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.check_expr_eq_type(&e, ty);
                 ty
             }
+            ExprKind::If(ref cond, ref then_expr, ref opt_else_expr) => self.check_then_else(
+                &cond,
+                then_expr,
+                opt_else_expr.as_ref().map(|e| &**e),
+                expr.span,
+                expected,
+            ),
             ExprKind::DropTemps(ref e) => self.check_expr_with_expectation(e, expected),
             ExprKind::Array(ref args) => self.check_expr_array(args, expected, expr),
             ExprKind::ConstBlock(ref anon_const) => self.to_const(anon_const).ty,
@@ -671,14 +679,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if self.ret_coercion.is_none() {
             self.tcx.sess.emit_err(ReturnStmtOutsideOfFnBody { span: expr.span });
         } else if let Some(ref e) = expr_opt {
-            if self.ret_coercion_span.borrow().is_none() {
-                *self.ret_coercion_span.borrow_mut() = Some(e.span);
+            if self.ret_coercion_span.get().is_none() {
+                self.ret_coercion_span.set(Some(e.span));
             }
             self.check_return_expr(e);
         } else {
             let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut();
-            if self.ret_coercion_span.borrow().is_none() {
-                *self.ret_coercion_span.borrow_mut() = Some(expr.span);
+            if self.ret_coercion_span.get().is_none() {
+                self.ret_coercion_span.set(Some(expr.span));
             }
             let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression);
             if let Some((fn_decl, _)) = self.get_fn_decl(expr.hir_id) {
@@ -738,8 +746,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err.emit();
     }
 
+    // A generic function for checking the 'then' and 'else' clauses in an 'if'
+    // or 'if-else' expression.
+    fn check_then_else(
+        &self,
+        cond_expr: &'tcx hir::Expr<'tcx>,
+        then_expr: &'tcx hir::Expr<'tcx>,
+        opt_else_expr: Option<&'tcx hir::Expr<'tcx>>,
+        sp: Span,
+        orig_expected: Expectation<'tcx>,
+    ) -> Ty<'tcx> {
+        let cond_ty = self.check_expr_has_type_or_error(cond_expr, self.tcx.types.bool, |_| {});
+
+        self.warn_if_unreachable(cond_expr.hir_id, then_expr.span, "block in `if` expression");
+
+        let cond_diverges = self.diverges.get();
+        self.diverges.set(Diverges::Maybe);
+
+        let expected = orig_expected.adjust_for_branches(self);
+        let then_ty = self.check_expr_with_expectation(then_expr, expected);
+        let then_diverges = self.diverges.get();
+        self.diverges.set(Diverges::Maybe);
+
+        // We've already taken the expected type's preferences
+        // into account when typing the `then` branch. To figure
+        // out the initial shot at a LUB, we thus only consider
+        // `expected` if it represents a *hard* constraint
+        // (`only_has_type`); otherwise, we just go with a
+        // fresh type variable.
+        let coerce_to_ty = expected.coercion_target_type(self, sp);
+        let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty);
+
+        coerce.coerce(self, &self.misc(sp), then_expr, then_ty);
+
+        if let Some(else_expr) = opt_else_expr {
+            let else_ty = self.check_expr_with_expectation(else_expr, expected);
+            let else_diverges = self.diverges.get();
+
+            let opt_suggest_box_span =
+                self.opt_suggest_box_span(else_expr.span, else_ty, orig_expected);
+            let if_cause =
+                self.if_cause(sp, then_expr, else_expr, then_ty, else_ty, opt_suggest_box_span);
+
+            coerce.coerce(self, &if_cause, else_expr, else_ty);
+
+            // We won't diverge unless both branches do (or the condition does).
+            self.diverges.set(cond_diverges | then_diverges & else_diverges);
+        } else {
+            self.if_fallback_coercion(sp, then_expr, &mut coerce, |hir_id, span| {
+                self.maybe_get_coercion_reason_if(hir_id, span)
+            });
+
+            // If the condition is false we can't diverge.
+            self.diverges.set(cond_diverges);
+        }
+
+        let result_ty = coerce.complete(self);
+        if cond_ty.references_error() { self.tcx.ty_error() } else { result_ty }
+    }
+
     /// Type check assignment expression `expr` of form `lhs = rhs`.
-    /// The expected type is `()` and is passsed to the function for the purposes of diagnostics.
+    /// The expected type is `()` and is passed to the function for the purposes of diagnostics.
     fn check_expr_assign(
         &self,
         expr: &'tcx hir::Expr<'tcx>,
@@ -764,17 +831,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
             if !lhs.is_syntactic_place_expr() {
                 // Do not suggest `if let x = y` as `==` is way more likely to be the intention.
-                if let hir::Node::Expr(hir::Expr {
-                    kind:
-                        ExprKind::Match(
-                            _,
-                            _,
-                            hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar,
-                        ),
-                    ..
-                }) = self.tcx.hir().get(
-                    self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(expr.hir_id)),
-                ) {
+                let mut span_err = || {
                     // Likely `if let` intended.
                     err.span_suggestion_verbose(
                         expr.span.shrink_to_lo(),
@@ -782,6 +839,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         "let ".to_string(),
                         applicability,
                     );
+                };
+                if let hir::Node::Expr(hir::Expr {
+                    kind: ExprKind::Match(_, _, hir::MatchSource::WhileDesugar),
+                    ..
+                }) = self.tcx.hir().get(
+                    self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(expr.hir_id)),
+                ) {
+                    span_err();
+                } else if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) =
+                    self.tcx.hir().get(self.tcx.hir().get_parent_node(expr.hir_id))
+                {
+                    span_err();
                 }
             }
             if eq {
@@ -883,7 +952,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Ok(method)
             }
             Err(error) => {
-                if segment.ident.name != kw::Invalid {
+                if segment.ident.name != kw::Empty {
                     self.report_extended_method_error(segment, span, args, rcvr_t, error);
                 }
                 Err(())
@@ -1124,7 +1193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             fields,
             base_expr.is_none(),
         );
-        if let &Some(ref base_expr) = base_expr {
+        if let Some(base_expr) = base_expr {
             // If check_expr_struct_fields hit an error, do not attempt to populate
             // 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.
@@ -1181,8 +1250,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // re-link the regions that EIfEO can erase.
         self.demand_eqtype(span, adt_ty_hint, adt_ty);
 
-        let (substs, adt_kind, kind_name) = match &adt_ty.kind() {
-            &ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()),
+        let (substs, adt_kind, kind_name) = match adt_ty.kind() {
+            ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()),
             _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields"),
         };
 
@@ -1380,19 +1449,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty,
         );
         match variant.ctor_kind {
-            CtorKind::Fn => {
-                err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty));
-                err.span_label(field.ident.span, "field does not exist");
-                err.span_label(
-                    ty_span,
-                    format!(
-                        "`{adt}` is a tuple {kind_name}, \
-                         use the appropriate syntax: `{adt}(/* fields */)`",
-                        adt = ty,
-                        kind_name = kind_name
-                    ),
-                );
-            }
+            CtorKind::Fn => match ty.kind() {
+                ty::Adt(adt, ..) if adt.is_enum() => {
+                    err.span_label(
+                        variant.ident.span,
+                        format!(
+                            "`{adt}::{variant}` defined here",
+                            adt = ty,
+                            variant = variant.ident,
+                        ),
+                    );
+                    err.span_label(field.ident.span, "field does not exist");
+                    err.span_label(
+                        ty_span,
+                        format!(
+                            "`{adt}::{variant}` is a tuple {kind_name}, \
+                             use the appropriate syntax: `{adt}::{variant}(/* fields */)`",
+                            adt = ty,
+                            variant = variant.ident,
+                            kind_name = kind_name
+                        ),
+                    );
+                }
+                _ => {
+                    err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty));
+                    err.span_label(field.ident.span, "field does not exist");
+                    err.span_label(
+                        ty_span,
+                        format!(
+                            "`{adt}` is a tuple {kind_name}, \
+                                 use the appropriate syntax: `{adt}(/* fields */)`",
+                            adt = ty,
+                            kind_name = kind_name
+                        ),
+                    );
+                }
+            },
             _ => {
                 // prevent all specified fields from being suggested
                 let skip_fields = skip_fields.iter().map(|ref x| x.ident.name);
@@ -1492,11 +1584,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         base: &'tcx hir::Expr<'tcx>,
         field: Ident,
     ) -> Ty<'tcx> {
+        debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
         let expr_t = self.check_expr(base);
         let expr_t = self.structurally_resolved_type(base.span, expr_t);
         let mut private_candidate = None;
         let mut autoderef = self.autoderef(expr.span, expr_t);
         while let Some((base_t, _)) = autoderef.next() {
+            debug!("base_t: {:?}", base_t);
             match base_t.kind() {
                 ty::Adt(base_def, substs) if !base_def.is_enum() => {
                     debug!("struct named {:?}", base_t);
@@ -1547,7 +1641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return field_ty;
         }
 
-        if field.name == kw::Invalid {
+        if field.name == kw::Empty {
         } else if self.method_exists(field, expr_t, expr.hir_id, true) {
             self.ban_take_value_of_method(expr, expr_t, field);
         } else if !expr_t.is_primitive_ty() {
@@ -1613,7 +1707,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
             field, base, expr, expr_t
         );
-        let mut err = self.no_such_field_err(field.span, field, expr_t);
+        let mut err = self.no_such_field_err(field, expr_t);
 
         match *expr_t.peel_refs().kind() {
             ty::Array(_, len) => {
@@ -1637,8 +1731,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if field.name == kw::Await {
             // We know by construction that `<expr>.await` is either on Rust 2015
             // or results in `ExprKind::Await`. Suggest switching the edition to 2018.
-            err.note("to `.await` a `Future`, switch to Rust 2018");
-            err.help("set `edition = \"2018\"` in `Cargo.toml`");
+            err.note("to `.await` a `Future`, switch to Rust 2018 or later");
+            err.help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION));
             err.note("for more on editions, read https://doc.rust-lang.org/edition-guide");
         }
 
@@ -1787,21 +1881,120 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    fn no_such_field_err<T: Display>(
+    fn no_such_field_err(
         &self,
-        span: Span,
-        field: T,
-        expr_t: &ty::TyS<'_>,
+        field: Ident,
+        expr_t: &'tcx ty::TyS<'tcx>,
     ) -> DiagnosticBuilder<'_> {
-        type_error_struct!(
+        let span = field.span;
+        debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t);
+
+        let mut err = type_error_struct!(
             self.tcx().sess,
-            span,
+            field.span,
             expr_t,
             E0609,
             "no field `{}` on type `{}`",
             field,
             expr_t
-        )
+        );
+
+        // try to add a suggestion in case the field is a nested field of a field of the Adt
+        if let Some((fields, substs)) = self.get_field_candidates(span, &expr_t) {
+            for candidate_field in fields.iter() {
+                if let Some(field_path) =
+                    self.check_for_nested_field(span, field, candidate_field, substs, vec![])
+                {
+                    let field_path_str = field_path
+                        .iter()
+                        .map(|id| id.name.to_ident_string())
+                        .collect::<Vec<String>>()
+                        .join(".");
+                    debug!("field_path_str: {:?}", field_path_str);
+
+                    err.span_suggestion_verbose(
+                        field.span.shrink_to_lo(),
+                        "one of the expressions' fields has a field of the same name",
+                        format!("{}.", field_path_str),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+        err
+    }
+
+    fn get_field_candidates(
+        &self,
+        span: Span,
+        base_t: Ty<'tcx>,
+    ) -> Option<(&Vec<ty::FieldDef>, SubstsRef<'tcx>)> {
+        debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t);
+
+        let mut autoderef = self.autoderef(span, base_t);
+        while let Some((base_t, _)) = autoderef.next() {
+            match base_t.kind() {
+                ty::Adt(base_def, substs) if !base_def.is_enum() => {
+                    let fields = &base_def.non_enum_variant().fields;
+                    // For compile-time reasons put a limit on number of fields we search
+                    if fields.len() > 100 {
+                        return None;
+                    }
+                    return Some((fields, substs));
+                }
+                _ => {}
+            }
+        }
+        None
+    }
+
+    /// This method is called after we have encountered a missing field error to recursively
+    /// search for the field
+    fn check_for_nested_field(
+        &self,
+        span: Span,
+        target_field: Ident,
+        candidate_field: &ty::FieldDef,
+        subst: SubstsRef<'tcx>,
+        mut field_path: Vec<Ident>,
+    ) -> Option<Vec<Ident>> {
+        debug!(
+            "check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}",
+            span, candidate_field, field_path
+        );
+
+        if candidate_field.ident == target_field {
+            Some(field_path)
+        } else if field_path.len() > 3 {
+            // For compile-time reasons and to avoid infinite recursion we only check for fields
+            // up to a depth of three
+            None
+        } else {
+            // recursively search fields of `candidate_field` if it's a ty::Adt
+
+            field_path.push(candidate_field.ident.normalize_to_macros_2_0());
+            let field_ty = candidate_field.ty(self.tcx, subst);
+            if let Some((nested_fields, subst)) = self.get_field_candidates(span, &field_ty) {
+                for field in nested_fields.iter() {
+                    let ident = field.ident.normalize_to_macros_2_0();
+                    if ident == target_field {
+                        return Some(field_path);
+                    } else {
+                        let field_path = field_path.clone();
+                        if let Some(path) = self.check_for_nested_field(
+                            span,
+                            target_field,
+                            field,
+                            subst,
+                            field_path,
+                        ) {
+                            return Some(path);
+                        }
+                    }
+                }
+            }
+            None
+        }
     }
 
     fn check_expr_index(