about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs119
-rw-r--r--src/test/ui/rfcs/rfc-2528-type-changing-struct-update/coerce-in-base-expr.rs28
-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.stderr23
4 files changed, 93 insertions, 78 deletions
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index dc9d76160c4..37d12b4ed5d 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -1561,73 +1561,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // 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 {
-                if let ty::Adt(adt, substs) = adt_ty.kind() && adt.is_struct() {
-                    // Make an ADT with fresh inference substitutions. This
-                    // will allow us to guide inference along so that, e.g.
+                if adt.is_struct() {
+                    // Make some fresh substitutions for our ADT type.
+                    let fresh_substs = self.fresh_substs_for_item(base_expr.span, adt.did());
+                    // We do subtyping on the FRU fields first, so we can
+                    // learn exactly what types we expect the base expr
+                    // needs constrained to be compatible with the struct
+                    // type we expect from the expectation value.
+                    let fru_tys = variant
+                        .fields
+                        .iter()
+                        .map(|f| {
+                            let fru_ty = self.normalize_associated_types_in(
+                                expr_span,
+                                self.field_ty(base_expr.span, f, fresh_substs),
+                            );
+                            let ident = self.tcx.adjust_ident(f.ident(self.tcx), 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)
+                                    }
+                                    Err(_) => {
+                                        // This should never happen, since we're just subtyping the
+                                        // remaining_fields, but it's fine to emit this, I guess.
+                                        self.report_mismatched_types(
+                                            &cause,
+                                            target_ty,
+                                            fru_ty,
+                                            FieldMisMatch(variant.name, ident.name),
+                                        )
+                                        .emit();
+                                    }
+                                }
+                            }
+                            self.resolve_vars_if_possible(fru_ty)
+                        })
+                        .collect();
+                    // The use of fresh substs that we have subtyped against
+                    // our base ADT type's fields allows us to guide inference
+                    // along so that, e.g.
                     // ```
-                    // let x = MyStruct<'a, B, const C: usize> {
-                    //    f: 1,
-                    //    ..Default::default()
+                    // MyStruct<'a, F1, F2, const C: usize> {
+                    //     f: F1,
+                    //     // Other fields that reference `'a`, `F2`, and `C`
+                    // }
+                    //
+                    // let x = MyStruct {
+                    //    f: 1usize,
+                    //    ..other_struct
                     // };
                     // ```
-                    // will have the default base expression constrained to
-                    // `MyStruct<'_, _, _>`, as opposed to just `_`... This
-                    // will allow us to then do a subtyping relation on all
-                    // of the `remaining_fields` below, per the RFC.
-                    let fresh_substs = self.fresh_substs_for_item(base_expr.span, adt.did());
+                    // will have the `other_struct` expression constrained to
+                    // `MyStruct<'a, _, F2, C>`, as opposed to just `_`...
+                    // This is important to allow coercions to happen in
+                    // `other_struct` itself. See `coerce-in-base-expr.rs`.
                     let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs);
-                    let base_ty = self.check_expr_has_type_or_error(
+                    self.check_expr_has_type_or_error(
                         base_expr,
-                        fresh_base_ty,
-                        |_| {
-                            error_happened = true;
-                        },
+                        self.resolve_vars_if_possible(fresh_base_ty),
+                        |_| {},
                     );
-                    let base_ty = self.shallow_resolve(base_ty);
-                    if let ty::Adt(base_adt, base_substs) = base_ty.kind() && adt == base_adt {
-                        variant
-                            .fields
-                            .iter()
-                            .map(|f| {
-                                let fru_ty = self.normalize_associated_types_in(
-                                    expr_span,
-                                    self.field_ty(base_expr.span, f, base_substs),
-                                );
-                                let ident = self
-                                    .tcx
-                                    .adjust_ident(f.ident(self.tcx), 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.name, ident.name),
-                                            )
-                                            .emit();
-                                        }
-                                    }
-                                }
-                                self.resolve_vars_if_possible(fru_ty)
-                            })
-                            .collect()
-                    } else {
-                        if !error_happened && !base_ty.references_error() {
-                            span_bug!(base_expr.span, "expected an error to have been reported in `check_expr_has_type_or_error`");
-                        }
-                        return;
-                    }
+                    fru_tys
                 } else {
                     // Check the base_expr, regardless of a bad expected adt_ty, so we can get
                     // type errors on that expression, too.
diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/coerce-in-base-expr.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/coerce-in-base-expr.rs
new file mode 100644
index 00000000000..75e48bf4a48
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/coerce-in-base-expr.rs
@@ -0,0 +1,28 @@
+// check-pass
+
+#![feature(type_changing_struct_update)]
+#![allow(incomplete_features)]
+
+use std::any::Any;
+
+struct Foo<A, B: ?Sized, C: ?Sized> {
+    a: A,
+    b: Box<B>,
+    c: Box<C>,
+}
+
+struct B;
+struct C;
+
+fn main() {
+    let y = Foo::<usize, dyn Any, dyn Any> {
+        a: 0,
+        b: Box::new(B),
+        ..Foo {
+            a: 0,
+            b: Box::new(B),
+            // C needs to be told to coerce to `Box<dyn Any>`
+            c: Box::new(C),
+        }
+    };
+}
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..6f31b1a9620 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
@@ -2,29 +2,20 @@ error[E0308]: mismatched types
   --> $DIR/type-generic-update.rs:46:11
    |
 LL |         ..m1
-   |           ^^ field type mismatch: Machine.state
+   |           ^^ expected `i32`, found `f64`
    |
-   = note: expected type `i32`
-              found type `f64`
+   = note: expected struct `Machine<'_, i32, _>`
+              found struct `Machine<'_, f64, _>`
 
 error[E0308]: mismatched types
   --> $DIR/type-generic-update.rs:51:11
    |
 LL |         ..m1
-   |           ^^ field type mismatch: Machine.state
+   |           ^^ expected `i32`, found `f64`
    |
-   = note: expected type `i32`
-              found type `f64`
+   = note: expected struct `Machine<'_, i32, i32>`
+              found struct `Machine<'_, f64, 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`.