From 7bde18a0f3dc52754eb52d09da0bc259b1a0e757 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Tue, 19 Oct 2021 11:08:12 +0800 Subject: [PATCH 1/4] implement type-changing-struct-update put the test dir in test/ui/rfcs --- compiler/rustc_middle/src/ty/error.rs | 3 + .../rustc_middle/src/ty/structural_impls.rs | 1 + compiler/rustc_typeck/src/check/expr.rs | 120 +++++++++++++----- .../type-changing-struct-update.md | 33 +++++ .../feature-gate.rs} | 2 + .../feature-gate.stderr} | 2 +- .../lifetime-update.rs | 43 +++++++ .../lifetime-update.stderr | 15 +++ .../type-generic-update.rs | 57 +++++++++ .../type-generic-update.stderr | 30 +++++ 10 files changed, 276 insertions(+), 30 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/type-changing-struct-update.md rename src/test/ui/{feature-gates/feature-gate-type_changing_struct_update.rs => rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs} (92%) rename src/test/ui/{feature-gates/feature-gate-type_changing_struct_update.stderr => rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr} (84%) create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.rs create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/lifetime-update.stderr create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.stderr diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 2bd9415171d7d..b14a69892657b 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), FixedArraySize(ExpectedFound), 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 d6069395474ab..0f8e80806e31e 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 3846aad2cfc10..25ad6b659a799 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) { 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 0000000000000..9909cf35b5b51 --- /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 { + field1: T, + field2: U, + } + + let base: Foo = Foo { + field1: String::from("hello"), + field2: 1234, + }; + let updated: Foo = 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 similarity index 92% rename from src/test/ui/feature-gates/feature-gate-type_changing_struct_update.rs rename to src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.rs index 520c1478f3234..d05ced724cc39 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 { 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 similarity index 84% rename from src/test/ui/feature-gates/feature-gate-type_changing_struct_update.stderr rename to src/test/ui/rfcs/rfc-2528-type-changing-struct-update/feature-gate.stderr index 9934fe6816430..e45ab02a9b7ff 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 0000000000000..df2fef55dd2d8 --- /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 = 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 0000000000000..5f93ad6e0279e --- /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 0000000000000..d8b1396a692a7 --- /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 = Machine { + state: State1, + message: Message1, + lt_str: "hello", + common_field: 2, + }; + // single type update + let m2: Machine = Machine { + state: State2, + ..m1 + }; + // multiple type update + let m3: Machine = Machine { + state: State2, + message: Message2, + ..m1 + }; +} + +fn fail_update() { + let m1: Machine = Machine { + state: 3.2, + message: 6.4, + lt_str: "hello", + common_field: 2, + }; + // single type update fail + let m2: Machine = Machine { + ..m1 + //~^ ERROR mismatched types [E0308] + }; + // multiple type update fail + let m3 = Machine:: { + ..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 0000000000000..fa8d6ee23d5ec --- /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`. From f3679bc23e0ae5998d5f0141a51b3d084fd66eab Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 29 Oct 2021 11:16:28 +0800 Subject: [PATCH 2/4] move the processing part of `base_expr` into `check_expr_struct_fields` --- compiler/rustc_typeck/src/check/expr.rs | 192 ++++++++---------- .../feature-gate.rs | 2 +- .../feature-gate.stderr | 2 +- 3 files changed, 92 insertions(+), 104 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 25ad6b659a799..4c7c9ed766deb 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, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorReported; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; @@ -1264,109 +1264,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); } - let (error_happened, mut remaining_fields) = self.check_expr_struct_fields( + self.check_expr_struct_fields( adt_ty, expected, expr.hir_id, qpath.span(), variant, fields, - base_expr.is_none(), + base_expr, expr.span, ); - if let Some(base_expr) = base_expr { - // If check_expr_struct_fields hit an error, do not attempt to populate - // 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 { - // 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(); - } - } - } 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); adt_ty } @@ -1379,9 +1287,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, variant: &'tcx ty::VariantDef, ast_fields: &'tcx [hir::ExprField<'tcx>], - check_completeness: bool, + base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, expr_span: Span, - ) -> (bool, FxHashSet) { + ) { let tcx = self.tcx; let adt_ty_hint = self @@ -1456,7 +1364,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .emit(); } - } else if check_completeness && !error_happened && !remaining_fields.is_empty() { + } + + // If check_expr_struct_fields hit an error, do not attempt to populate + // 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 { + return; + } + + 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, + }); + }; + 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); + 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 + }) + .collect() + } + _ => { + return self + .report_mismatched_types( + &self.misc(base_expr.span), + adt_ty, + base_ty, + Mismatch, + ) + .emit(); + } + } + } 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); + } 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) }); @@ -1464,11 +1454,9 @@ 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.clone()); + self.report_missing_fields(adt_ty, span, remaining_fields); } } - - (error_happened, remaining_fields.iter().map(|(ident, _)| ident.clone()).collect()) } fn check_struct_fields_on_error( 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 d05ced724cc39..81888ee1d4fdc 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,11 +17,11 @@ fn update_to_state2() { common_field1: "hello", common_field2: 2, }; + // FIXME: this should trigger feature gate let m2: Machine = Machine { state: State2, ..m1 //~ ERROR mismatched types }; - // FIXME: this should trigger feature gate 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 e45ab02a9b7ff..19059593844d6 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,5 @@ error[E0308]: mismatched types - --> $DIR/feature-gate.rs:22:11 + --> $DIR/feature-gate.rs:23:11 | LL | ..m1 | ^^ expected struct `State2`, found struct `State1` From 1ab2616b4da64218d359a87fdd043edf4167cf76 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 29 Oct 2021 20:08:30 +0800 Subject: [PATCH 3/4] Add feature trigger and correct `is_struct` check --- compiler/rustc_typeck/src/check/expr.rs | 177 +++++++++++------- .../feature-gate.rs | 5 +- .../feature-gate.stderr | 16 +- .../type-generic-update.rs | 1 - .../type-generic-update.stderr | 11 +- 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 4c7c9ed766deb..1d4a43c07b1e6 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 81888ee1d4fdc..1e8b99ba5647a 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 = 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 19059593844d6..2217b8c049863 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 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` found struct `Machine` -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 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..631c8f83c91e7 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`. From 926892ddc0b75b50f5d0a3483d829e501aa8e895 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 5 Nov 2021 09:30:49 +0800 Subject: [PATCH 4/4] Add feature trigger and enable is_struct check --- compiler/rustc_typeck/src/check/expr.rs | 188 ++++++++---------- .../type-generic-update.rs | 1 + .../type-generic-update.stderr | 11 +- 3 files changed, 96 insertions(+), 104 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 1d4a43c07b1e6..ab53d64e9bfb5 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -36,7 +36,8 @@ 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; +use rustc_middle::ty::error::TypeError::{FieldMisMatch, Sorts}; +use rustc_middle::ty::relate::expected_found_bool; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::Ty; use rustc_middle::ty::TypeFoldable; @@ -1375,60 +1376,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(base_expr) = base_expr { - 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( + // 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() => { + match base_ty.kind() { + ty::Adt(base_adt, base_subs) if 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_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, @@ -1436,63 +1414,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant.ident.name, ident.name, ), - )) - } + ) + .emit(), } } - } - 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(); + fru_ty + }) + .collect() + } + _ => { + return self + .report_mismatched_types( + &self.misc(base_expr.span), + adt_ty, + base_ty, + Sorts(expected_found_bool(true, adt_ty, base_ty)), + ) + .emit(); } } } _ => { - err = self.demand_suptype_diag(base_expr.span, expected_ty, ty); + return self + .tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); } } - is_struct = adt.is_struct(); - if is_struct && fru_tys.is_none() { - fru_tys = Some( - variant - .fields - .iter() - .map(|f| { - self.normalize_associated_types_in( - expr_span, - f.ty(self.tcx, substs), - ) - }) - .collect(), - ) - } } else { - 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); - } + self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { + let base_ty = self.check_expr(base_expr); + let same_adt = match (adt_ty.kind(), base_ty.kind()) { + (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true, + _ => false, + }; + if self.tcx.sess.is_nightly_build() && same_adt { + feature_err( + &self.tcx.sess.parse_sess, + sym::type_changing_struct_update, + base_expr.span, + "type changing struct updating is experimental", + ) + .emit(); + } + }); + 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); } 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/type-generic-update.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/type-generic-update.rs index dae1241d35a5f..d8b1396a692a7 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,6 +50,7 @@ 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 631c8f83c91e7..fa8d6ee23d5ec 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,6 +16,15 @@ LL | ..m1 = note: expected type `i32` found type `f64` -error: aborting due to 2 previous errors +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`.