about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSparrowLii <liyuan179@huawei.com>2021-10-19 11:08:12 +0800
committerSparrowLii <liyuan179@huawei.com>2021-10-28 14:17:15 +0800
commit7bde18a0f3dc52754eb52d09da0bc259b1a0e757 (patch)
tree932856e492ecc8144b95f7535a6868a503e52cf7
parent41d8c94d454f23239715a6433df79e46df8bce04 (diff)
downloadrust-7bde18a0f3dc52754eb52d09da0bc259b1a0e757.tar.gz
rust-7bde18a0f3dc52754eb52d09da0bc259b1a0e757.zip
implement type-changing-struct-update
put the test dir in test/ui/rfcs
-rw-r--r--compiler/rustc_middle/src/ty/error.rs3
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs1
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs120
-rw-r--r--src/doc/unstable-book/src/language-features/type-changing-struct-update.md33
-rw-r--r--src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs (renamed from src/test/ui/feature-gates/feature-gate-type_changing_struct_update.rs)2
-rw-r--r--src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr (renamed from src/test/ui/feature-gates/feature-gate-type_changing_struct_update.stderr)2
-rw-r--r--src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.rs43
-rw-r--r--src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.stderr15
-rw-r--r--src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs57
-rw-r--r--src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr30
10 files changed, 276 insertions, 30 deletions
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 2bd9415171d..b14a6989265 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -42,6 +42,7 @@ pub enum TypeError<'tcx> {
     TupleSize(ExpectedFound<usize>),
     FixedArraySize(ExpectedFound<u64>),
     ArgCount,
+    FieldMisMatch(Symbol, Symbol),
 
     RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>),
     RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>),
@@ -134,6 +135,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
                 pluralize!(values.found)
             ),
             ArgCount => write!(f, "incorrect number of function parameters"),
+            FieldMisMatch(adt, field) => write!(f, "field type mismatch: {}.{}", adt, field),
             RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"),
             RegionsInsufficientlyPolymorphic(br, _) => write!(
                 f,
@@ -224,6 +226,7 @@ impl<'tcx> TypeError<'tcx> {
             | ArgumentMutability(_)
             | TupleSize(_)
             | ArgCount
+            | FieldMisMatch(..)
             | RegionsDoesNotOutlive(..)
             | RegionsInsufficientlyPolymorphic(..)
             | RegionsOverlyPolymorphic(..)
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index d6069395474..0f8e80806e3 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -602,6 +602,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
             TupleSize(x) => TupleSize(x),
             FixedArraySize(x) => FixedArraySize(x),
             ArgCount => ArgCount,
+            FieldMisMatch(x, y) => FieldMisMatch(x, y),
             RegionsDoesNotOutlive(a, b) => {
                 return tcx.lift((a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b));
             }
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 3846aad2cfc..25ad6b659a7 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -23,7 +23,7 @@ use crate::type_error_struct;
 
 use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
 use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::ErrorReported;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
@@ -33,8 +33,10 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{ExprKind, QPath};
 use rustc_infer::infer;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+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::subst::SubstsRef;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::TypeFoldable;
@@ -1262,7 +1264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() });
         }
 
-        let error_happened = self.check_expr_struct_fields(
+        let (error_happened, mut remaining_fields) = self.check_expr_struct_fields(
             adt_ty,
             expected,
             expr.hir_id,
@@ -1277,32 +1279,92 @@ 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, |_| {});
-                match adt_ty.kind() {
-                    ty::Adt(adt, substs) if adt.is_struct() => {
-                        let fru_field_types = adt
-                            .non_enum_variant()
-                            .fields
-                            .iter()
-                            .map(|f| {
-                                self.normalize_associated_types_in(
-                                    expr.span,
-                                    f.ty(self.tcx, substs),
-                                )
-                            })
-                            .collect();
-
-                        self.typeck_results
-                            .borrow_mut()
-                            .fru_field_types_mut()
-                            .insert(expr.hir_id, fru_field_types);
+                // FIXME: We are currently creating two branches here in order to maintain
+                // consistency. But they should be merged as much as possible.
+                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 fru_field_types = 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 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: Needs better diagnostics here
+                                            Err(_) => self
+                                                .report_mismatched_types(
+                                                    &cause,
+                                                    target_ty,
+                                                    fru_ty,
+                                                    FieldMisMatch(variant.ident.name, ident.name),
+                                                )
+                                                .emit(),
+                                        }
+                                    }
+                                    fru_ty
+                                })
+                                .collect();
+
+                            self.typeck_results
+                                .borrow_mut()
+                                .fru_field_types_mut()
+                                .insert(expr.hir_id, fru_field_types);
+                        }
+                        _ => {
+                            self.report_mismatched_types(
+                                &self.misc(base_expr.span),
+                                adt_ty,
+                                base_ty,
+                                Mismatch,
+                            )
+                            .emit();
+                        }
                     }
-                    _ => {
-                        self.tcx
-                            .sess
-                            .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
+                } else {
+                    self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {});
+                    match adt_ty.kind() {
+                        ty::Adt(adt, substs) if adt.is_struct() => {
+                            let fru_field_types = adt
+                                .non_enum_variant()
+                                .fields
+                                .iter()
+                                .map(|f| {
+                                    self.normalize_associated_types_in(
+                                        expr.span,
+                                        f.ty(self.tcx, substs),
+                                    )
+                                })
+                                .collect();
+
+                            self.typeck_results
+                                .borrow_mut()
+                                .fru_field_types_mut()
+                                .insert(expr.hir_id, fru_field_types);
+                        }
+                        _ => {
+                            self.tcx.sess.emit_err(FunctionalRecordUpdateOnNonStruct {
+                                span: base_expr.span,
+                            });
+                        }
                     }
-                }
+                };
             }
         }
         self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized);
@@ -1319,7 +1381,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ast_fields: &'tcx [hir::ExprField<'tcx>],
         check_completeness: bool,
         expr_span: Span,
-    ) -> bool {
+    ) -> (bool, FxHashSet<Ident>) {
         let tcx = self.tcx;
 
         let adt_ty_hint = self
@@ -1402,11 +1464,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if inaccessible_remaining_fields {
                 self.report_inaccessible_fields(adt_ty, span);
             } else {
-                self.report_missing_fields(adt_ty, span, remaining_fields);
+                self.report_missing_fields(adt_ty, span, remaining_fields.clone());
             }
         }
 
-        error_happened
+        (error_happened, remaining_fields.iter().map(|(ident, _)| ident.clone()).collect())
     }
 
     fn check_struct_fields_on_error(
diff --git a/src/doc/unstable-book/src/language-features/type-changing-struct-update.md b/src/doc/unstable-book/src/language-features/type-changing-struct-update.md
new file mode 100644
index 00000000000..9909cf35b5b
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/type-changing-struct-update.md
@@ -0,0 +1,33 @@
+# `type_changing_struct_update`
+
+The tracking issue for this feature is: [#86555]
+
+[#86555]: https://github.com/rust-lang/rust/issues/86555
+
+------------------------
+
+This implements [RFC2528]. When turned on, you can create instances of the same struct
+that have different generic type or lifetime parameters.
+
+[RFC2528]: https://github.com/rust-lang/rfcs/blob/master/text/2528-type-changing-struct-update-syntax.md
+
+```rust
+#![allow(unused_variables, dead_code)]
+#![feature(type_changing_struct_update)]
+
+fn main () {
+    struct Foo<T, U> {
+        field1: T,
+        field2: U,
+    }
+
+    let base: Foo<String, i32> = Foo {
+        field1: String::from("hello"),
+        field2: 1234,
+    };
+    let updated: Foo<f64, i32> = Foo {
+        field1: 3.14,
+        ..base
+    };
+}
+```
diff --git a/src/test/ui/feature-gates/feature-gate-type_changing_struct_update.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs
index 520c1478f32..d05ced724cc 100644
--- a/src/test/ui/feature-gates/feature-gate-type_changing_struct_update.rs
+++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs
@@ -1,3 +1,5 @@
+// gate-test-type_changing_struct_update
+
 #[derive(Debug)]
 struct Machine<S> {
     state: S,
diff --git a/src/test/ui/feature-gates/feature-gate-type_changing_struct_update.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr
index 9934fe68164..e45ab02a9b7 100644
--- a/src/test/ui/feature-gates/feature-gate-type_changing_struct_update.stderr
+++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/feature-gate-type_changing_struct_update.rs:20:11
+  --> $DIR/feature-gate.rs:22:11
    |
 LL |         ..m1
    |           ^^ expected struct `State2`, found struct `State1`
diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.rs
new file mode 100644
index 00000000000..df2fef55dd2
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.rs
@@ -0,0 +1,43 @@
+#![feature(type_changing_struct_update)]
+#![allow(incomplete_features)]
+
+#[derive(Clone)]
+struct Machine<'a, S> {
+    state: S,
+    lt_str: &'a str,
+    common_field: i32,
+}
+
+#[derive(Clone)]
+struct State1;
+#[derive(Clone)]
+struct State2;
+
+fn update_to_state2() {
+    let s = String::from("hello");
+    let m1: Machine<State1> = Machine {
+        state: State1,
+        lt_str: &s,
+                //~^ ERROR `s` does not live long enough [E0597]
+                // FIXME: The error here actually comes from line 34. The
+                // span of the error message should be corrected to line 34
+        common_field: 2,
+    };
+    // update lifetime
+    let m3: Machine<'static, State1> = Machine {
+        lt_str: "hello, too",
+        ..m1.clone()
+    };
+    // update lifetime and type
+    let m4: Machine<'static, State2> = Machine {
+        state: State2,
+        lt_str: "hello, again",
+        ..m1.clone()
+    };
+    // updating to `static should fail.
+    let m2: Machine<'static, State1> = Machine {
+        ..m1
+    };
+}
+
+fn main() {}
diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.stderr
new file mode 100644
index 00000000000..5f93ad6e027
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.stderr
@@ -0,0 +1,15 @@
+error[E0597]: `s` does not live long enough
+  --> $DIR/lifetime-update.rs:20:17
+   |
+LL |         lt_str: &s,
+   |                 ^^ borrowed value does not live long enough
+...
+LL |     let m2: Machine<'static, State1> = Machine {
+   |             ------------------------ type annotation requires that `s` is borrowed for `'static`
+...
+LL | }
+   | - `s` dropped here while still borrowed
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
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
new file mode 100644
index 00000000000..d8b1396a692
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs
@@ -0,0 +1,57 @@
+#![feature(type_changing_struct_update)]
+#![allow(incomplete_features)]
+
+struct Machine<'a, S, M> {
+    state: S,
+    message: M,
+    lt_str: &'a str,
+    common_field: i32,
+}
+
+struct State1;
+struct State2;
+
+struct Message1;
+struct Message2;
+
+fn update() {
+    let m1: Machine<State1, Message1> = Machine {
+        state: State1,
+        message: Message1,
+        lt_str: "hello",
+        common_field: 2,
+    };
+    // single type update
+    let m2: Machine<State2, Message1> = Machine {
+        state: State2,
+        ..m1
+    };
+    // multiple type update
+    let m3: Machine<State2, Message2> = Machine {
+        state: State2,
+        message: Message2,
+        ..m1
+    };
+}
+
+fn fail_update() {
+    let m1: Machine<f64, f64> = Machine {
+        state: 3.2,
+        message: 6.4,
+        lt_str: "hello",
+        common_field: 2,
+    };
+    // single type update fail
+    let m2: Machine<i32, f64> = Machine {
+        ..m1
+        //~^ ERROR mismatched types [E0308]
+    };
+    // multiple type update fail
+    let m3 = Machine::<i32, i32> {
+        ..m1
+        //~^ ERROR mismatched types [E0308]
+        //~| ERROR mismatched types [E0308]
+    };
+}
+
+fn main() {}
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
new file mode 100644
index 00000000000..fa8d6ee23d5
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr
@@ -0,0 +1,30 @@
+error[E0308]: mismatched types
+  --> $DIR/type-generic-update.rs:46:11
+   |
+LL |         ..m1
+   |           ^^ field type mismatch: Machine.state
+   |
+   = note: expected type `i32`
+              found type `f64`
+
+error[E0308]: mismatched types
+  --> $DIR/type-generic-update.rs:51:11
+   |
+LL |         ..m1
+   |           ^^ field type mismatch: Machine.state
+   |
+   = 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
+
+For more information about this error, try `rustc --explain E0308`.