about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSparrowLii <liyuan179@huawei.com>2021-10-29 20:08:30 +0800
committerSparrowLii <liyuan179@huawei.com>2021-10-29 20:08:30 +0800
commit1ab2616b4da64218d359a87fdd043edf4167cf76 (patch)
treed0233fe9d87bd86ca10ff3891cc91b6156f4e22f
parentf3679bc23e0ae5998d5f0141a51b3d084fd66eab (diff)
downloadrust-1ab2616b4da64218d359a87fdd043edf4167cf76.tar.gz
rust-1ab2616b4da64218d359a87fdd043edf4167cf76.zip
Add feature trigger and correct `is_struct` check
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs177
-rw-r--r--src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs5
-rw-r--r--src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr16
-rw-r--r--src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs1
-rw-r--r--src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr11
5 files changed, 129 insertions, 81 deletions
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 4c7c9ed766d..1d4a43c07b1 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -36,11 +36,12 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
 use rustc_infer::infer::InferOk;
 use rustc_middle::ty;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
-use rustc_middle::ty::error::TypeError::{FieldMisMatch, Mismatch};
+use rustc_middle::ty::error::TypeError::FieldMisMatch;
 use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::TypeFoldable;
 use rustc_middle::ty::{AdtKind, Visibility};
+use rustc_session::parse::feature_err;
 use rustc_span::edition::LATEST_STABLE_EDITION;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::lev_distance::find_best_match_for_name;
@@ -1374,78 +1375,124 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         if let Some(base_expr) = base_expr {
-            // FIXME: We are currently creating two branches here in order to maintain
-            // consistency. But they should be merged as much as possible.
-            let fru_tys = if self.tcx.features().type_changing_struct_update {
-                let base_ty = self.check_expr(base_expr);
-                match (adt_ty.kind(), base_ty.kind()) {
-                    (ty::Adt(adt, substs), ty::Adt(base_adt, base_subs)) if adt == base_adt => {
-                        if !adt.is_struct() {
-                            self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct {
-                                span: base_expr.span,
-                            });
-                        };
+            let expected = if self.tcx.features().type_changing_struct_update {
+                NoExpectation
+            } else {
+                ExpectHasType(adt_ty)
+            };
+            let mut ty = self.check_expr_with_expectation(base_expr, expected);
+
+            let expected_ty = expected.to_option(&self).unwrap_or(adt_ty);
+            // While we don't allow *arbitrary* coercions here, we *do* allow
+            // coercions from ! to `expected`.
+            if ty.is_never() {
+                assert!(
+                    !self.typeck_results.borrow().adjustments().contains_key(base_expr.hir_id),
+                    "expression with never type wound up being adjusted"
+                );
+                let adj_ty = self.next_ty_var(TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::AdjustmentType,
+                    span: base_expr.span,
+                });
+                self.apply_adjustments(
+                    base_expr,
+                    vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }],
+                );
+                ty = adj_ty;
+            }
+            let cause = self.misc(base_expr.span);
+            let mut fru_tys = None;
+            let mut err = None;
+            let is_struct;
+
+            if let ty::Adt(adt, substs) = expected_ty.kind() {
+                match ty.kind() {
+                    ty::Adt(base_adt, base_subs) if adt == base_adt => {
+                        if self.tcx.features().type_changing_struct_update {
+                            let tys = variant
+                                .fields
+                                .iter()
+                                .map(|f| {
+                                    let fru_ty = self.normalize_associated_types_in(
+                                        expr_span,
+                                        self.field_ty(base_expr.span, f, base_subs),
+                                    );
+                                    let ident = self.tcx.adjust_ident(f.ident, variant.def_id);
+                                    if let Some(_) = remaining_fields.remove(&ident) {
+                                        let target_ty = self.field_ty(base_expr.span, f, substs);
+                                        match self.at(&cause, self.param_env).sup(target_ty, fru_ty)
+                                        {
+                                            Ok(InferOk { obligations, value: () }) => {
+                                                self.register_predicates(obligations)
+                                            }
+                                            // FIXME: Need better diagnostics for `FieldMisMatch` error
+                                            Err(_) => {
+                                                if err.is_none() {
+                                                    err = Some(self.report_mismatched_types(
+                                                        &cause,
+                                                        target_ty,
+                                                        fru_ty,
+                                                        FieldMisMatch(
+                                                            variant.ident.name,
+                                                            ident.name,
+                                                        ),
+                                                    ))
+                                                }
+                                            }
+                                        }
+                                    }
+                                    fru_ty
+                                })
+                                .collect();
+                            fru_tys = Some(tys);
+                        } else {
+                            err = self.demand_suptype_diag(base_expr.span, expected_ty, ty);
+                            if err.is_some() && self.tcx.sess.is_nightly_build() {
+                                feature_err(
+                                    &self.tcx.sess.parse_sess,
+                                    sym::type_changing_struct_update,
+                                    base_expr.span,
+                                    "type changing struct updating is experimental",
+                                )
+                                .emit();
+                            }
+                        }
+                    }
+                    _ => {
+                        err = self.demand_suptype_diag(base_expr.span, expected_ty, ty);
+                    }
+                }
+                is_struct = adt.is_struct();
+                if is_struct && fru_tys.is_none() {
+                    fru_tys = Some(
                         variant
                             .fields
                             .iter()
                             .map(|f| {
-                                let fru_ty = self.normalize_associated_types_in(
+                                self.normalize_associated_types_in(
                                     expr_span,
-                                    self.field_ty(base_expr.span, f, base_subs),
-                                );
-                                let ident = self.tcx.adjust_ident(f.ident, variant.def_id);
-                                if let Some(_) = remaining_fields.remove(&ident) {
-                                    let target_ty = self.field_ty(base_expr.span, f, substs);
-                                    let cause = self.misc(base_expr.span);
-                                    match self.at(&cause, self.param_env).sup(target_ty, fru_ty) {
-                                        Ok(InferOk { obligations, value: () }) => {
-                                            self.register_predicates(obligations)
-                                        }
-                                        // FIXME: Need better diagnostics for `FieldMisMatch` error
-                                        Err(_) => self
-                                            .report_mismatched_types(
-                                                &cause,
-                                                target_ty,
-                                                fru_ty,
-                                                FieldMisMatch(variant.ident.name, ident.name),
-                                            )
-                                            .emit(),
-                                    }
-                                }
-                                fru_ty
+                                    f.ty(self.tcx, substs),
+                                )
                             })
-                            .collect()
-                    }
-                    _ => {
-                        return self
-                            .report_mismatched_types(
-                                &self.misc(base_expr.span),
-                                adt_ty,
-                                base_ty,
-                                Mismatch,
-                            )
-                            .emit();
-                    }
+                            .collect(),
+                    )
                 }
             } else {
-                self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {});
-                match adt_ty.kind() {
-                    ty::Adt(adt, substs) if adt.is_struct() => variant
-                        .fields
-                        .iter()
-                        .map(|f| {
-                            self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs))
-                        })
-                        .collect(),
-                    _ => {
-                        return self
-                            .tcx
-                            .sess
-                            .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
-                    }
-                }
-            };
-            self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
+                err = self.demand_suptype_diag(base_expr.span, expected_ty, ty);
+                is_struct = false;
+            }
+            if let Some(mut err) = err {
+                let expr = base_expr.peel_drop_temps();
+                self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None);
+                err.emit();
+            }
+            if let Some(fru_tys) = fru_tys {
+                self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
+            }
+            if !is_struct {
+                let e = FunctionalRecordUpdateOnNonStruct { span: base_expr.span };
+                self.tcx.sess.emit_err(e);
+            }
         } else if kind_name != "union" && !remaining_fields.is_empty() {
             let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| {
                 !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs
index 81888ee1d4f..1e8b99ba564 100644
--- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs
+++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs
@@ -17,10 +17,11 @@ fn update_to_state2() {
         common_field1: "hello",
         common_field2: 2,
     };
-    // FIXME: this should trigger feature gate
     let m2: Machine<State2> = Machine {
         state: State2,
-        ..m1 //~ ERROR mismatched types
+        ..m1
+        //~^ ERROR type changing struct updating is experimental [E0658]
+        //~| ERROR mismatched types [E0308]
     };
     assert_eq!(State2, m2.state);
 }
diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr
index 19059593844..2217b8c0498 100644
--- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr
+++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr
@@ -1,5 +1,14 @@
+error[E0658]: type changing struct updating is experimental
+  --> $DIR/feature-gate.rs:22:11
+   |
+LL |         ..m1
+   |           ^^
+   |
+   = note: see issue #86555 <https://github.com/rust-lang/rust/issues/86555> for more information
+   = help: add `#![feature(type_changing_struct_update)]` to the crate attributes to enable
+
 error[E0308]: mismatched types
-  --> $DIR/feature-gate.rs:23:11
+  --> $DIR/feature-gate.rs:22:11
    |
 LL |         ..m1
    |           ^^ expected struct `State2`, found struct `State1`
@@ -7,6 +16,7 @@ LL |         ..m1
    = note: expected struct `Machine<State2>`
               found struct `Machine<State1>`
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0308`.
+Some errors have detailed explanations: E0308, E0658.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs
index d8b1396a692..dae1241d35a 100644
--- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs
+++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs
@@ -50,7 +50,6 @@ fn fail_update() {
     let m3 = Machine::<i32, i32> {
         ..m1
         //~^ ERROR mismatched types [E0308]
-        //~| ERROR mismatched types [E0308]
     };
 }
 
diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr
index fa8d6ee23d5..631c8f83c91 100644
--- a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr
+++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr
@@ -16,15 +16,6 @@ LL |         ..m1
    = note: expected type `i32`
               found type `f64`
 
-error[E0308]: mismatched types
-  --> $DIR/type-generic-update.rs:51:11
-   |
-LL |         ..m1
-   |           ^^ field type mismatch: Machine.message
-   |
-   = note: expected type `i32`
-              found type `f64`
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0308`.