From 784e28e218a12fcf8fd9caaca62798d7c18068b2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 11 Jun 2022 13:21:38 -0700 Subject: [PATCH] Subtype FRU fields first in type_changing_struct_update --- compiler/rustc_typeck/src/check/expr.rs | 119 +++++++++--------- .../coerce-in-base-expr.rs | 28 +++++ .../type-generic-update.rs | 1 - .../type-generic-update.stderr | 23 ++-- 4 files changed, 93 insertions(+), 78 deletions(-) create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/coerce-in-base-expr.rs diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index dc9d76160c4e9..37d12b4ed5da4 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 0000000000000..75e48bf4a482a --- /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: A, + b: Box, + c: Box, +} + +struct B; +struct C; + +fn main() { + let y = Foo:: { + a: 0, + b: Box::new(B), + ..Foo { + a: 0, + b: Box::new(B), + // C needs to be told to coerce to `Box` + 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 d8b1396a692a7..dae1241d35a5f 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:: { ..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 fa8d6ee23d5ec..6f31b1a962078 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`.