From 1ab2616b4da64218d359a87fdd043edf4167cf76 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 29 Oct 2021 20:08:30 +0800 Subject: [PATCH] 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 4c7c9ed766de..1d4a43c07b1e 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 81888ee1d4fd..1e8b99ba5647 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 19059593844d..2217b8c04986 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 d8b1396a692a..dae1241d35a5 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 fa8d6ee23d5e..631c8f83c91e 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`.