From 6c00e546679fc653396377e226a30fb0b86fd5e6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 3 Jun 2022 12:54:11 -0700 Subject: [PATCH] Guide inference along during type_changing_struct_update --- compiler/rustc_typeck/src/check/expr.rs | 28 +++++++++++++++-- .../issue-96878.rs | 31 +++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-96878.rs diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 48bbd4d76ea..33cff62772e 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1557,9 +1557,27 @@ 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 { - let base_ty = self.check_expr(base_expr); match adt_ty.kind() { ty::Adt(adt, substs) if adt.is_struct() => { + // Make an ADT with fresh inference substitutions. This + // will allow us to guide inference along so that, e.g. + // ``` + // let x = MyStruct<'a, B, const C: usize> { + // f: 1, + // ..Default::default() + // }; + // ``` + // 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()); + let base_ty = self.check_expr_has_type_or_error( + base_expr, + self.tcx.mk_adt(*adt, fresh_substs), + |_| {}, + ); + let base_ty = self.shallow_resolve(base_ty); match base_ty.kind() { ty::Adt(base_adt, base_subs) if adt == base_adt => { variant @@ -1585,7 +1603,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_predicates(obligations) } // FIXME: Need better diagnostics for `FieldMisMatch` error - Err(_) => { + Err(type_error) => { + debug!("check_expr_struct_fields: {fru_ty} sub {target_ty} failed: {type_error:?}"); self.report_mismatched_types( &cause, target_ty, @@ -1596,7 +1615,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - fru_ty + self.resolve_vars_if_possible(fru_ty) }) .collect() } @@ -1613,6 +1632,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } _ => { + // Check the base_expr, regardless of a bad expected adt_ty, so we can get + // type errors on that expression, too. + self.check_expr(base_expr); self.tcx .sess .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-96878.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-96878.rs new file mode 100644 index 00000000000..3dfbef0ee90 --- /dev/null +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-96878.rs @@ -0,0 +1,31 @@ +// check-pass + +#![feature(type_changing_struct_update)] +#![allow(incomplete_features)] + +use std::borrow::Cow; +use std::marker::PhantomData; + +#[derive(Default)] +struct NonGeneric { + field1: usize, +} + +#[derive(Default)] +struct Generic { + field1: T, + field2: U, +} + +#[derive(Default)] +struct MoreGeneric<'a, const N: usize> { + // If only `for [u32; N]: Default`... + field1: PhantomData<[u32; N]>, + field2: Cow<'a, str>, +} + +fn main() { + let default1 = NonGeneric { ..Default::default() }; + let default2: Generic = Generic { ..Default::default() }; + let default3: MoreGeneric<'static, 12> = MoreGeneric { ..Default::default() }; +}