From b63aea59a2a61c5dee5387031c9f08f3bea3576a Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Wed, 21 Sep 2022 16:46:41 +0800 Subject: [PATCH 1/4] demand_base_struct --- compiler/rustc_hir_analysis/src/check/mod.rs | 254 ++++++++++++++++++ compiler/rustc_hir_typeck/src/demand.rs | 16 ++ compiler/rustc_hir_typeck/src/expr.rs | 7 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 13 + compiler/rustc_infer/src/infer/at.rs | 26 ++ compiler/rustc_infer/src/infer/base_struct.rs | 129 +++++++++ compiler/rustc_infer/src/infer/combine.rs | 5 + compiler/rustc_infer/src/infer/mod.rs | 1 + compiler/rustc_infer/src/infer/sub.rs | 3 +- compiler/rustc_middle/src/ty/context.rs | 11 + compiler/rustc_middle/src/ty/error.rs | 3 - compiler/rustc_middle/src/ty/relate.rs | 29 +- 12 files changed, 479 insertions(+), 18 deletions(-) create mode 100644 compiler/rustc_infer/src/infer/base_struct.rs diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 2e7b102576407..fc112bbea5133 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -113,6 +113,260 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option { tcx.calculate_dtor(def_id, dropck::check_drop_impl) } +/// If this `DefId` is a "primary tables entry", returns +/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`. +/// +/// If this function returns `Some`, then `typeck_results(def_id)` will +/// succeed; if it returns `None`, then `typeck_results(def_id)` may or +/// may not succeed. In some cases where this function returns `None` +/// (notably closures), `typeck_results(def_id)` would wind up +/// redirecting to the owning function. +fn primary_body_of( + tcx: TyCtxt<'_>, + id: hir::HirId, +) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> { + match tcx.hir().get(id) { + Node::Item(item) => match item.kind { + hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => { + Some((body, Some(ty), None)) + } + hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))), + _ => None, + }, + Node::TraitItem(item) => match item.kind { + hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)), + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { + Some((body, None, Some(sig))) + } + _ => None, + }, + Node::ImplItem(item) => match item.kind { + hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)), + hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))), + _ => None, + }, + Node::AnonConst(constant) => Some((constant.body, None, None)), + _ => None, + } +} + +fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + // Closures' typeck results come from their outermost function, + // as they are part of the same "inference environment". + let typeck_root_def_id = tcx.typeck_root_def_id(def_id); + if typeck_root_def_id != def_id { + return tcx.has_typeck_results(typeck_root_def_id); + } + + if let Some(def_id) = def_id.as_local() { + let id = tcx.hir().local_def_id_to_hir_id(def_id); + primary_body_of(tcx, id).is_some() + } else { + false + } +} + +fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet { + &*tcx.typeck(def_id).used_trait_imports +} + +fn typeck_const_arg<'tcx>( + tcx: TyCtxt<'tcx>, + (did, param_did): (LocalDefId, DefId), +) -> &ty::TypeckResults<'tcx> { + let fallback = move || tcx.type_of(param_did); + typeck_with_fallback(tcx, did, fallback) +} + +fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { + if let Some(param_did) = tcx.opt_const_param_of(def_id) { + tcx.typeck_const_arg((def_id, param_did)) + } else { + let fallback = move || tcx.type_of(def_id.to_def_id()); + typeck_with_fallback(tcx, def_id, fallback) + } +} + +/// Used only to get `TypeckResults` for type inference during error recovery. +/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors. +fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { + let fallback = move || { + let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id)); + tcx.ty_error_with_message(span, "diagnostic only typeck table used") + }; + typeck_with_fallback(tcx, def_id, fallback) +} + +fn typeck_with_fallback<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + fallback: impl Fn() -> Ty<'tcx> + 'tcx, +) -> &'tcx ty::TypeckResults<'tcx> { + // Closures' typeck results come from their outermost function, + // as they are part of the same "inference environment". + let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(); + if typeck_root_def_id != def_id { + return tcx.typeck(typeck_root_def_id); + } + + let id = tcx.hir().local_def_id_to_hir_id(def_id); + let span = tcx.hir().span(id); + + // Figure out what primary body this item has. + let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| { + span_bug!(span, "can't type-check body of {:?}", def_id); + }); + let body = tcx.hir().body(body_id); + + let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { + let param_env = tcx.param_env(def_id); + let mut fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig { + let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { + let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); + >::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None) + } else { + tcx.fn_sig(def_id) + }; + + check_abi(tcx, id, span, fn_sig.abi()); + + // Compute the function signature from point of view of inside the fn. + let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); + let fn_sig = inh.normalize_associated_types_in( + body.value.span, + body_id.hir_id, + param_env, + fn_sig, + ); + check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0 + } else { + let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); + let expected_type = body_ty + .and_then(|ty| match ty.kind { + hir::TyKind::Infer => Some(>::ast_ty_to_ty(&fcx, ty)), + _ => None, + }) + .unwrap_or_else(|| match tcx.hir().get(id) { + Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::ConstBlock(ref anon_const), + .. + }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }), + Node::Ty(&hir::Ty { + kind: hir::TyKind::Typeof(ref anon_const), .. + }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }), + Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) + | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { + let operand_ty = asm + .operands + .iter() + .filter_map(|(op, _op_sp)| match op { + hir::InlineAsmOperand::Const { anon_const } + if anon_const.hir_id == id => + { + // Inline assembly constants must be integers. + Some(fcx.next_int_var()) + } + hir::InlineAsmOperand::SymFn { anon_const } + if anon_const.hir_id == id => + { + Some(fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span, + })) + } + _ => None, + }) + .next(); + operand_ty.unwrap_or_else(fallback) + } + _ => fallback(), + }, + _ => fallback(), + }); + + let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type); + fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); + + // Gather locals in statics (because of block expressions). + GatherLocalsVisitor::new(&fcx).visit_body(body); + + fcx.check_expr_coercable_to_type(&body.value, expected_type, None); + + fcx.write_ty(id, expected_type); + + fcx + }; + + let fallback_has_occurred = fcx.type_inference_fallback(); + + // Even though coercion casts provide type hints, we check casts after fallback for + // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. + fcx.check_casts(); + fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); + + // Closure and generator analysis may run after fallback + // because they don't constrain other type variables. + // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now) + let prev_constness = fcx.param_env.constness(); + fcx.param_env = fcx.param_env.without_const(); + fcx.closure_analyze(body); + fcx.param_env = fcx.param_env.with_constness(prev_constness); + assert!(fcx.deferred_call_resolutions.borrow().is_empty()); + // Before the generator analysis, temporary scopes shall be marked to provide more + // precise information on types to be captured. + fcx.resolve_rvalue_scopes(def_id.to_def_id()); + fcx.resolve_generator_interiors(def_id.to_def_id()); + + for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { + let ty = fcx.normalize_ty(span, ty); + fcx.require_type_is_sized(ty, span, code); + } + + fcx.resolve_base_expr(); + + fcx.select_all_obligations_or_error(); + + if !fcx.infcx.is_tainted_by_errors() { + fcx.check_transmutes(); + } + + fcx.check_asms(); + + fcx.infcx.skip_region_resolution(); + + fcx.resolve_type_vars_in_body(body) + }); + + // Consistency check our TypeckResults instance can hold all ItemLocalIds + // it will need to hold. + assert_eq!(typeck_results.hir_owner, id.owner); + + typeck_results +} + +/// When `check_fn` is invoked on a generator (i.e., a body that +/// includes yield), it returns back some information about the yield +/// points. +struct GeneratorTypes<'tcx> { + /// Type of generator argument / values returned by `yield`. + resume_ty: Ty<'tcx>, + + /// Type of value that is yielded. + yield_ty: Ty<'tcx>, + + /// Types that are captured (see `GeneratorInterior` for more). + interior: Ty<'tcx>, + + /// Indicates if the generator is movable or static (immovable). + movability: hir::Movability, +} /// Given a `DefId` for an opaque type in return position, find its parent item's return /// expressions. fn get_owner_return_paths<'tcx>( diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 16febfc46da90..22d9944fb4f5b 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -174,6 +174,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (expected, Some(err)) } + #[instrument(skip(self), level = "debug")] + pub fn demand_base_struct( + &self, + cause: &ObligationCause<'tcx>, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + ) -> Option> { + match self.at(cause, self.param_env).base_struct(expected, actual) { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations); + None + } + Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)), + } + } + fn annotate_expected_due_to_let_ty( &self, err: &mut Diagnostic, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 682dbab56bc15..4bcb625336af9 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -39,7 +39,6 @@ use rustc_infer::infer::InferOk; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::error::TypeError::FieldMisMatch; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable}; use rustc_session::errors::ExprParenthesesNeeded; @@ -1677,8 +1676,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fru_ty, FieldMisMatch(variant.name, ident.name), ) - .emit(); - } + .emit(); } } } self.resolve_vars_if_possible(fru_ty) @@ -1705,9 +1703,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs); self.check_expr_has_type_or_error( base_expr, - self.resolve_vars_if_possible(fresh_base_ty), + fresh_base_ty, |_| {}, ); + self.typeck_results.borrow_mut().base_expr_backup_mut().insert(base_expr.hir_id, (fresh_base_ty, adt_ty)); fru_tys } else { // Check the base_expr, regardless of a bad expected adt_ty, so we can get diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 7563c543d3f19..6034e33662ee4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -605,6 +605,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + pub(in super::super) fn resolve_base_expr(&self) { + for (_, (base_ty, adt_ty)) in self.typeck_results.borrow().base_expr_backup().iter() { + let base_ty = self.resolve_vars_if_possible(*base_ty); + if base_ty.has_infer_types() { + if let Some(mut err) = + self.demand_base_struct(&self.misc(DUMMY_SP), *adt_ty, base_ty) + { + err.emit(); + } + } + } + } + #[instrument(skip(self), level = "debug")] pub(in super::super) fn select_all_obligations_or_error(&self) { let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self); diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 5ff3779fa1438..76e49916ef451 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -218,6 +218,17 @@ impl<'a, 'tcx> At<'a, 'tcx> { self.trace(expected, actual).glb(expected, actual) } + pub fn base_struct( + self, + expected: T, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.trace(expected, actual).base_struct(expected, actual) + } + /// Sets the "trace" values that will be used for /// error-reporting, but doesn't actually perform any operation /// yet (this is useful when you want to set the trace using @@ -259,6 +270,21 @@ impl<'a, 'tcx> Trace<'a, 'tcx> { }) } + #[instrument(skip(self), level = "debug")] + pub fn base_struct(self, a: T, b: T) -> InferResult<'tcx, ()> + where + T: Relate<'tcx>, + { + let Trace { at, trace, a_is_expected } = self; + at.infcx.commit_if_ok(|_| { + let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types); + fields + .base_struct(a_is_expected) + .relate(a, b) + .map(move |_| InferOk { value: (), obligations: fields.obligations }) + }) + } + /// Makes `a == b`; the expectation is set by the call to /// `trace()`. #[instrument(skip(self), level = "debug")] diff --git a/compiler/rustc_infer/src/infer/base_struct.rs b/compiler/rustc_infer/src/infer/base_struct.rs new file mode 100644 index 0000000000000..36aa6f75a8329 --- /dev/null +++ b/compiler/rustc_infer/src/infer/base_struct.rs @@ -0,0 +1,129 @@ +use std::{iter, mem}; +use crate::infer::sub::Sub; +use rustc_middle::ty; +use rustc_middle::ty::relate::{Cause, Relate, relate_generic_arg, RelateResult, TypeRelation}; +use rustc_middle::ty::{Subst, SubstsRef, Ty, TyCtxt}; +use rustc_span::def_id::DefId; + +pub struct BaseStruct<'combine, 'infcx, 'tcx> { + sub: Sub<'combine, 'infcx, 'tcx>, +} + +impl<'combine, 'infcx, 'tcx> BaseStruct<'combine, 'infcx, 'tcx> { + pub fn new( + sub: Sub<'combine, 'infcx, 'tcx>, + ) -> Self { + BaseStruct { sub } + } +} + +impl<'tcx> TypeRelation<'tcx> for BaseStruct<'_, '_, 'tcx> { + fn tag(&self) -> &'static str { + "BaseStruct" + } + + #[inline(always)] + fn tcx(&self) -> TyCtxt<'tcx> { + self.sub.tcx() + } + + #[inline(always)] + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.sub.param_env() + } + + #[inline(always)] + fn a_is_expected(&self) -> bool { + self.sub.a_is_expected() + } + + #[inline(always)] + fn with_cause(&mut self, cause: Cause, f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + let old_cause = mem::replace(&mut self.sub.fields.cause, Some(cause)); + let r = f(self); + self.sub.fields.cause = old_cause; + r + } + + fn relate_item_substs( + &mut self, + item_def_id: DefId, + a_subst: SubstsRef<'tcx>, + b_subst: SubstsRef<'tcx>, + ) -> RelateResult<'tcx, SubstsRef<'tcx>> { + debug!( + "relate_item_substs(item_def_id={:?}, a_subst={:?}, b_subst={:?})", + item_def_id, a_subst, b_subst + ); + + let tcx = self.tcx(); + let variances = tcx.variances_of(item_def_id); + + let mut cached_ty = None; + let params = iter::zip(a_subst, b_subst).enumerate().map(|(i, (a, b))| { + let cached_ty = + *cached_ty.get_or_insert_with(|| tcx.bound_type_of(item_def_id).subst(tcx, a_subst)); + relate_generic_arg(&mut self.sub, variances, cached_ty, a, b, i).or_else(|_| { + Ok(b) + }) + }); + + tcx.mk_substs(params) + } + + #[inline(always)] + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + self.sub.relate_with_variance(variance, info, a, b) + } + + #[inline(always)] + #[instrument(skip(self), level = "debug")] + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + match (a.kind(), b.kind()) { + (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) if a_def == b_def => { + let substs = self.relate_item_substs(a_def.did(), a_substs, b_substs)?; + Ok(self.tcx().mk_adt(a_def, substs)) + } + _ => bug!("not adt ty: {:?} and {:?}", a, b) + } + } + + #[inline(always)] + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + self.sub.regions(a, b) + } + + #[inline(always)] + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.sub.consts(a, b) + } + + #[inline(always)] + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + self.sub.binders(a, b) + } +} diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index a973bf54b055e..0d814930585e7 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -40,6 +40,7 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitable}; use rustc_middle::ty::{IntType, UintType}; use rustc_span::{Span, DUMMY_SP}; +use crate::infer::base_struct::BaseStruct; #[derive(Clone)] pub struct CombineFields<'infcx, 'tcx> { @@ -300,6 +301,10 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { Sub::new(self, a_is_expected) } + pub fn base_struct<'a>(&'a mut self, a_is_expected: bool) -> BaseStruct<'a, 'infcx, 'tcx> { + BaseStruct::new(Sub::new(self, a_is_expected)) + } + pub fn lub<'a>(&'a mut self, a_is_expected: bool) -> Lub<'a, 'infcx, 'tcx> { Lub::new(self, a_is_expected) } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c2eecd9e87a38..2aca47b6ed436 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -71,6 +71,7 @@ pub mod resolve; mod sub; pub mod type_variable; mod undo_log; +mod base_struct; #[must_use] #[derive(Debug)] diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index 97354ba5d1bd1..469e8d68aac49 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -11,8 +11,7 @@ use std::mem; /// Ensures `a` is made a subtype of `b`. Returns `a` on success. pub struct Sub<'combine, 'a, 'tcx> { - fields: &'combine mut CombineFields<'a, 'tcx>, - a_is_expected: bool, + fields: &'combine mut CombineFields<'a, 'tcx>, a_is_expected: bool, } impl<'combine, 'infcx, 'tcx> Sub<'combine, 'infcx, 'tcx> { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index fc706b890d5fb..3ac64152d3d49 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -515,6 +515,8 @@ pub struct TypeckResults<'tcx> { /// MIR construction and hence is not serialized to metadata. fru_field_types: ItemLocalMap>>, + base_expr_backup: ItemLocalMap<(Ty<'tcx>, Ty<'tcx>)>, + /// For every coercion cast we add the HIR node ID of the cast /// expression to this set. coercion_casts: ItemLocalSet, @@ -599,6 +601,7 @@ impl<'tcx> TypeckResults<'tcx> { closure_kind_origins: Default::default(), liberated_fn_sigs: Default::default(), fru_field_types: Default::default(), + base_expr_backup: Default::default(), coercion_casts: Default::default(), used_trait_imports: Lrc::new(Default::default()), tainted_by_errors: None, @@ -837,6 +840,14 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.fru_field_types } } + pub fn base_expr_backup(&self) -> LocalTableInContext<'_, (Ty<'tcx>, Ty<'tcx>)> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.base_expr_backup } + } + + pub fn base_expr_backup_mut(&mut self) -> LocalTableInContextMut<'_, (Ty<'tcx>, Ty<'tcx>)> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.base_expr_backup } + } + pub fn is_coercion_cast(&self, hir_id: hir::HirId) -> bool { validate_hir_id_for_typeck_results(self.hir_owner, hir_id); self.coercion_casts.contains(&hir_id.local_id) diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 4e6cdb786025e..7ef7deeb48999 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -44,7 +44,6 @@ pub enum TypeError<'tcx> { TupleSize(ExpectedFound), FixedArraySize(ExpectedFound), ArgCount, - FieldMisMatch(Symbol, Symbol), RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>), @@ -147,7 +146,6 @@ 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"), // Actually naming the region here is a bit confusing because context is lacking RegionsInsufficientlyPolymorphic(..) => { @@ -237,7 +235,6 @@ impl<'tcx> TypeError<'tcx> { | ArgumentMutability(_) | TupleSize(_) | ArgCount - | FieldMisMatch(..) | RegionsDoesNotOutlive(..) | RegionsInsufficientlyPolymorphic(..) | RegionsOverlyPolymorphic(..) diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index c083a405e3cfb..b148c2fd06da9 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -156,20 +156,31 @@ pub fn relate_substs_with_variances<'tcx, R: TypeRelation<'tcx>>( let mut cached_ty = None; let params = iter::zip(a_subst, b_subst).enumerate().map(|(i, (a, b))| { - let variance = variances[i]; - let variance_info = if variance == ty::Invariant { - let ty = - *cached_ty.get_or_insert_with(|| tcx.bound_type_of(ty_def_id).subst(tcx, a_subst)); - ty::VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() } - } else { - ty::VarianceDiagInfo::default() - }; - relation.relate_with_variance(variance, variance_info, a, b) + let cached_ty = + *cached_ty.get_or_insert_with(|| tcx.bound_type_of(ty_def_id).subst(tcx, a_subst)); + relate_generic_arg(relation, variances, cached_ty, a, b, i) }); tcx.mk_substs(params) } +pub fn relate_generic_arg<'tcx, R: TypeRelation<'tcx>>( + relation: &mut R, + variances: &[ty::Variance], + ty: Ty<'tcx>, + a: GenericArg<'tcx>, + b: GenericArg<'tcx>, + index: usize, +) -> RelateResult<'tcx, GenericArg<'tcx>> { + let variance = variances[index]; + let variance_info = if variance == ty::Invariant { + ty::VarianceDiagInfo::Invariant { ty, param_index: index.try_into().unwrap() } + } else { + ty::VarianceDiagInfo::default() + }; + relation.relate_with_variance(variance, variance_info, a, b) +} + impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> { fn relate>( relation: &mut R, From a04088b1fc6f0f614383d8f5994da77c2d182c1a Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Wed, 21 Sep 2022 20:24:27 +0800 Subject: [PATCH 2/4] simplify base_struct function --- compiler/rustc_hir_typeck/src/demand.rs | 8 +- compiler/rustc_infer/src/infer/at.rs | 26 --- compiler/rustc_infer/src/infer/base_struct.rs | 151 +++++------------- compiler/rustc_infer/src/infer/combine.rs | 5 - compiler/rustc_infer/src/infer/mod.rs | 2 +- compiler/rustc_middle/src/ty/relate.rs | 1 + 6 files changed, 47 insertions(+), 146 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 22d9944fb4f5b..eec79bc6f9274 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,4 +1,9 @@ use crate::FnCtxt; +use rustc_infer::infer::base_struct::base_struct; +use rustc_infer::infer::InferOk; +use rustc_middle::middle::stability::EvalResult; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::ObligationCause; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; @@ -181,7 +186,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, actual: Ty<'tcx>, ) -> Option> { - match self.at(cause, self.param_env).base_struct(expected, actual) { + let at = self.at(cause, self.param_env); + match base_struct(at, expected, actual) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); None diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 76e49916ef451..5ff3779fa1438 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -218,17 +218,6 @@ impl<'a, 'tcx> At<'a, 'tcx> { self.trace(expected, actual).glb(expected, actual) } - pub fn base_struct( - self, - expected: T, - actual: T, - ) -> InferResult<'tcx, ()> - where - T: ToTrace<'tcx>, - { - self.trace(expected, actual).base_struct(expected, actual) - } - /// Sets the "trace" values that will be used for /// error-reporting, but doesn't actually perform any operation /// yet (this is useful when you want to set the trace using @@ -270,21 +259,6 @@ impl<'a, 'tcx> Trace<'a, 'tcx> { }) } - #[instrument(skip(self), level = "debug")] - pub fn base_struct(self, a: T, b: T) -> InferResult<'tcx, ()> - where - T: Relate<'tcx>, - { - let Trace { at, trace, a_is_expected } = self; - at.infcx.commit_if_ok(|_| { - let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types); - fields - .base_struct(a_is_expected) - .relate(a, b) - .map(move |_| InferOk { value: (), obligations: fields.obligations }) - }) - } - /// Makes `a == b`; the expectation is set by the call to /// `trace()`. #[instrument(skip(self), level = "debug")] diff --git a/compiler/rustc_infer/src/infer/base_struct.rs b/compiler/rustc_infer/src/infer/base_struct.rs index 36aa6f75a8329..78836999f12c5 100644 --- a/compiler/rustc_infer/src/infer/base_struct.rs +++ b/compiler/rustc_infer/src/infer/base_struct.rs @@ -1,129 +1,54 @@ -use std::{iter, mem}; +use crate::infer::at::{At, ToTrace}; use crate::infer::sub::Sub; +use crate::infer::{InferOk, InferResult}; use rustc_middle::ty; -use rustc_middle::ty::relate::{Cause, Relate, relate_generic_arg, RelateResult, TypeRelation}; -use rustc_middle::ty::{Subst, SubstsRef, Ty, TyCtxt}; +use rustc_middle::ty::relate::{relate_generic_arg, RelateResult, TypeRelation}; +use rustc_middle::ty::{GenericArg, SubstsRef, Ty}; use rustc_span::def_id::DefId; +use std::iter; -pub struct BaseStruct<'combine, 'infcx, 'tcx> { - sub: Sub<'combine, 'infcx, 'tcx>, -} -impl<'combine, 'infcx, 'tcx> BaseStruct<'combine, 'infcx, 'tcx> { - pub fn new( - sub: Sub<'combine, 'infcx, 'tcx>, - ) -> Self { - BaseStruct { sub } - } +pub fn base_struct<'a, 'tcx>(at: At<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, ()> { + let trace = ToTrace::to_trace(at.infcx.tcx, at.cause, true, a, b); + at.infcx.commit_if_ok(|_| { + let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types); + let mut sub = Sub::new(&mut fields, true); + base_struct_tys(&mut sub, a, b) + .map(move |_| InferOk { value: (), obligations: fields.obligations }) + }) } -impl<'tcx> TypeRelation<'tcx> for BaseStruct<'_, '_, 'tcx> { - fn tag(&self) -> &'static str { - "BaseStruct" - } - - #[inline(always)] - fn tcx(&self) -> TyCtxt<'tcx> { - self.sub.tcx() - } - - #[inline(always)] - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.sub.param_env() - } - - #[inline(always)] - fn a_is_expected(&self) -> bool { - self.sub.a_is_expected() - } - - #[inline(always)] - fn with_cause(&mut self, cause: Cause, f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - let old_cause = mem::replace(&mut self.sub.fields.cause, Some(cause)); - let r = f(self); - self.sub.fields.cause = old_cause; - r +pub fn base_struct_tys<'tcx>(sub: &mut Sub<'_, '_, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { + match (a.kind(), b.kind()) { + (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) if a_def == b_def => { + base_struct_substs(sub, a_def.did(), a_substs, b_substs)?; + Ok(()) + } + _ => bug!("not adt ty: {:?} and {:?}", a, b), } +} - fn relate_item_substs( - &mut self, - item_def_id: DefId, - a_subst: SubstsRef<'tcx>, - b_subst: SubstsRef<'tcx>, - ) -> RelateResult<'tcx, SubstsRef<'tcx>> { - debug!( +fn base_struct_substs<'tcx>( + sub: &mut Sub<'_, '_, 'tcx>, + item_def_id: DefId, + a_subst: SubstsRef<'tcx>, + b_subst: SubstsRef<'tcx>, +) -> RelateResult<'tcx, ()> { + debug!( "relate_item_substs(item_def_id={:?}, a_subst={:?}, b_subst={:?})", item_def_id, a_subst, b_subst ); - let tcx = self.tcx(); - let variances = tcx.variances_of(item_def_id); + let tcx = sub.tcx(); + let variances = tcx.variances_of(item_def_id); - let mut cached_ty = None; - let params = iter::zip(a_subst, b_subst).enumerate().map(|(i, (a, b))| { - let cached_ty = - *cached_ty.get_or_insert_with(|| tcx.bound_type_of(item_def_id).subst(tcx, a_subst)); - relate_generic_arg(&mut self.sub, variances, cached_ty, a, b, i).or_else(|_| { - Ok(b) - }) - }); + let mut cached_ty = None; + iter::zip(a_subst, b_subst).enumerate().for_each(|(i, (a, b))| { + let cached_ty = *cached_ty + .get_or_insert_with(|| tcx.bound_type_of(item_def_id).subst(tcx, a_subst)); + let _arg: RelateResult<'tcx, GenericArg<'tcx>> = + relate_generic_arg(sub, variances, cached_ty, a, b, i).or_else(|_| Ok(b)); + }); - tcx.mk_substs(params) - } - - #[inline(always)] - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - self.sub.relate_with_variance(variance, info, a, b) - } - - #[inline(always)] - #[instrument(skip(self), level = "debug")] - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - match (a.kind(), b.kind()) { - (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) if a_def == b_def => { - let substs = self.relate_item_substs(a_def.did(), a_substs, b_substs)?; - Ok(self.tcx().mk_adt(a_def, substs)) - } - _ => bug!("not adt ty: {:?} and {:?}", a, b) - } - } - - #[inline(always)] - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - self.sub.regions(a, b) - } - - #[inline(always)] - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - self.sub.consts(a, b) - } - - #[inline(always)] - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - self.sub.binders(a, b) - } + Ok(()) } diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 0d814930585e7..a973bf54b055e 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -40,7 +40,6 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitable}; use rustc_middle::ty::{IntType, UintType}; use rustc_span::{Span, DUMMY_SP}; -use crate::infer::base_struct::BaseStruct; #[derive(Clone)] pub struct CombineFields<'infcx, 'tcx> { @@ -301,10 +300,6 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { Sub::new(self, a_is_expected) } - pub fn base_struct<'a>(&'a mut self, a_is_expected: bool) -> BaseStruct<'a, 'infcx, 'tcx> { - BaseStruct::new(Sub::new(self, a_is_expected)) - } - pub fn lub<'a>(&'a mut self, a_is_expected: bool) -> Lub<'a, 'infcx, 'tcx> { Lub::new(self, a_is_expected) } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 2aca47b6ed436..70e85b1120c8e 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -71,7 +71,7 @@ pub mod resolve; mod sub; pub mod type_variable; mod undo_log; -mod base_struct; +pub mod base_struct; #[must_use] #[derive(Debug)] diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index b148c2fd06da9..ad7d37587be29 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -164,6 +164,7 @@ pub fn relate_substs_with_variances<'tcx, R: TypeRelation<'tcx>>( tcx.mk_substs(params) } +#[inline] pub fn relate_generic_arg<'tcx, R: TypeRelation<'tcx>>( relation: &mut R, variances: &[ty::Variance], From 3bea6bcbd77df3074cce098751002d94d7aaa5ee Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 11 Nov 2022 12:09:08 +0800 Subject: [PATCH 3/4] optimize and add test --- compiler/rustc_hir_analysis/src/check/mod.rs | 254 ------------------ compiler/rustc_hir_typeck/src/demand.rs | 14 +- compiler/rustc_hir_typeck/src/expr.rs | 27 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 7 +- compiler/rustc_hir_typeck/src/lib.rs | 4 + compiler/rustc_infer/src/infer/base_struct.rs | 24 +- compiler/rustc_infer/src/infer/mod.rs | 2 +- compiler/rustc_infer/src/infer/sub.rs | 3 +- compiler/rustc_middle/src/ty/context.rs | 14 +- .../issue-101970.rs | 40 +++ .../issue-101970.stderr | 20 ++ 11 files changed, 99 insertions(+), 310 deletions(-) create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-101970.rs create mode 100644 src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-101970.stderr diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index fc112bbea5133..2e7b102576407 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -113,260 +113,6 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option { tcx.calculate_dtor(def_id, dropck::check_drop_impl) } -/// If this `DefId` is a "primary tables entry", returns -/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`. -/// -/// If this function returns `Some`, then `typeck_results(def_id)` will -/// succeed; if it returns `None`, then `typeck_results(def_id)` may or -/// may not succeed. In some cases where this function returns `None` -/// (notably closures), `typeck_results(def_id)` would wind up -/// redirecting to the owning function. -fn primary_body_of( - tcx: TyCtxt<'_>, - id: hir::HirId, -) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> { - match tcx.hir().get(id) { - Node::Item(item) => match item.kind { - hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => { - Some((body, Some(ty), None)) - } - hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))), - _ => None, - }, - Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)), - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - Some((body, None, Some(sig))) - } - _ => None, - }, - Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)), - hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))), - _ => None, - }, - Node::AnonConst(constant) => Some((constant.body, None, None)), - _ => None, - } -} - -fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // Closures' typeck results come from their outermost function, - // as they are part of the same "inference environment". - let typeck_root_def_id = tcx.typeck_root_def_id(def_id); - if typeck_root_def_id != def_id { - return tcx.has_typeck_results(typeck_root_def_id); - } - - if let Some(def_id) = def_id.as_local() { - let id = tcx.hir().local_def_id_to_hir_id(def_id); - primary_body_of(tcx, id).is_some() - } else { - false - } -} - -fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet { - &*tcx.typeck(def_id).used_trait_imports -} - -fn typeck_const_arg<'tcx>( - tcx: TyCtxt<'tcx>, - (did, param_did): (LocalDefId, DefId), -) -> &ty::TypeckResults<'tcx> { - let fallback = move || tcx.type_of(param_did); - typeck_with_fallback(tcx, did, fallback) -} - -fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { - if let Some(param_did) = tcx.opt_const_param_of(def_id) { - tcx.typeck_const_arg((def_id, param_did)) - } else { - let fallback = move || tcx.type_of(def_id.to_def_id()); - typeck_with_fallback(tcx, def_id, fallback) - } -} - -/// Used only to get `TypeckResults` for type inference during error recovery. -/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors. -fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { - let fallback = move || { - let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id)); - tcx.ty_error_with_message(span, "diagnostic only typeck table used") - }; - typeck_with_fallback(tcx, def_id, fallback) -} - -fn typeck_with_fallback<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - fallback: impl Fn() -> Ty<'tcx> + 'tcx, -) -> &'tcx ty::TypeckResults<'tcx> { - // Closures' typeck results come from their outermost function, - // as they are part of the same "inference environment". - let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(); - if typeck_root_def_id != def_id { - return tcx.typeck(typeck_root_def_id); - } - - let id = tcx.hir().local_def_id_to_hir_id(def_id); - let span = tcx.hir().span(id); - - // Figure out what primary body this item has. - let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| { - span_bug!(span, "can't type-check body of {:?}", def_id); - }); - let body = tcx.hir().body(body_id); - - let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { - let param_env = tcx.param_env(def_id); - let mut fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig { - let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - >::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None) - } else { - tcx.fn_sig(def_id) - }; - - check_abi(tcx, id, span, fn_sig.abi()); - - // Compute the function signature from point of view of inside the fn. - let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); - let fn_sig = inh.normalize_associated_types_in( - body.value.span, - body_id.hir_id, - param_env, - fn_sig, - ); - check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0 - } else { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - let expected_type = body_ty - .and_then(|ty| match ty.kind { - hir::TyKind::Infer => Some(>::ast_ty_to_ty(&fcx, ty)), - _ => None, - }) - .unwrap_or_else(|| match tcx.hir().get(id) { - Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::ConstBlock(ref anon_const), - .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Ty(&hir::Ty { - kind: hir::TyKind::Typeof(ref anon_const), .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) - | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { - let operand_ty = asm - .operands - .iter() - .filter_map(|(op, _op_sp)| match op { - hir::InlineAsmOperand::Const { anon_const } - if anon_const.hir_id == id => - { - // Inline assembly constants must be integers. - Some(fcx.next_int_var()) - } - hir::InlineAsmOperand::SymFn { anon_const } - if anon_const.hir_id == id => - { - Some(fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - })) - } - _ => None, - }) - .next(); - operand_ty.unwrap_or_else(fallback) - } - _ => fallback(), - }, - _ => fallback(), - }); - - let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type); - fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); - - // Gather locals in statics (because of block expressions). - GatherLocalsVisitor::new(&fcx).visit_body(body); - - fcx.check_expr_coercable_to_type(&body.value, expected_type, None); - - fcx.write_ty(id, expected_type); - - fcx - }; - - let fallback_has_occurred = fcx.type_inference_fallback(); - - // Even though coercion casts provide type hints, we check casts after fallback for - // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - fcx.check_casts(); - fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); - - // Closure and generator analysis may run after fallback - // because they don't constrain other type variables. - // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now) - let prev_constness = fcx.param_env.constness(); - fcx.param_env = fcx.param_env.without_const(); - fcx.closure_analyze(body); - fcx.param_env = fcx.param_env.with_constness(prev_constness); - assert!(fcx.deferred_call_resolutions.borrow().is_empty()); - // Before the generator analysis, temporary scopes shall be marked to provide more - // precise information on types to be captured. - fcx.resolve_rvalue_scopes(def_id.to_def_id()); - fcx.resolve_generator_interiors(def_id.to_def_id()); - - for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { - let ty = fcx.normalize_ty(span, ty); - fcx.require_type_is_sized(ty, span, code); - } - - fcx.resolve_base_expr(); - - fcx.select_all_obligations_or_error(); - - if !fcx.infcx.is_tainted_by_errors() { - fcx.check_transmutes(); - } - - fcx.check_asms(); - - fcx.infcx.skip_region_resolution(); - - fcx.resolve_type_vars_in_body(body) - }); - - // Consistency check our TypeckResults instance can hold all ItemLocalIds - // it will need to hold. - assert_eq!(typeck_results.hir_owner, id.owner); - - typeck_results -} - -/// When `check_fn` is invoked on a generator (i.e., a body that -/// includes yield), it returns back some information about the yield -/// points. -struct GeneratorTypes<'tcx> { - /// Type of generator argument / values returned by `yield`. - resume_ty: Ty<'tcx>, - - /// Type of value that is yielded. - yield_ty: Ty<'tcx>, - - /// Types that are captured (see `GeneratorInterior` for more). - interior: Ty<'tcx>, - - /// Indicates if the generator is movable or static (immovable). - movability: hir::Movability, -} /// Given a `DefId` for an opaque type in return position, find its parent item's return /// expressions. fn get_owner_return_paths<'tcx>( diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index eec79bc6f9274..dc0a3c7295fc1 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,14 +1,10 @@ use crate::FnCtxt; -use rustc_infer::infer::base_struct::base_struct; -use rustc_infer::infer::InferOk; -use rustc_middle::middle::stability::EvalResult; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::ObligationCause; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; +use rustc_infer::infer::base_struct::base_struct; use rustc_infer::infer::InferOk; use rustc_middle::lint::in_external_macro; use rustc_middle::middle::stability::EvalResult; @@ -183,16 +179,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn demand_base_struct( &self, cause: &ObligationCause<'tcx>, - expected: Ty<'tcx>, - actual: Ty<'tcx>, + adt_ty: Ty<'tcx>, + base_ty: Ty<'tcx>, ) -> Option> { let at = self.at(cause, self.param_env); - match base_struct(at, expected, actual) { + match base_struct(at, adt_ty, base_ty) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); None } - Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)), + Err(e) => Some(self.err_ctxt().report_mismatched_types(&cause, adt_ty, base_ty, e)), } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 4bcb625336af9..d69eb4d513d7d 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1667,16 +1667,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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.err_ctxt() - .report_mismatched_types( - &cause, - target_ty, - fru_ty, - FieldMisMatch(variant.name, ident.name), - ) - .emit(); } + bug!("subtype fresh substs failed") + } } } self.resolve_vars_if_possible(fru_ty) @@ -1701,12 +1693,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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); - self.check_expr_has_type_or_error( - base_expr, - fresh_base_ty, - |_| {}, - ); - self.typeck_results.borrow_mut().base_expr_backup_mut().insert(base_expr.hir_id, (fresh_base_ty, adt_ty)); + self.check_expr_has_type_or_error(base_expr, fresh_base_ty, |_| {}); + let fresh_base_ty = self.resolve_vars_if_possible(fresh_base_ty); + if fresh_base_ty.needs_infer() { + self.typeck_results.borrow_mut().base_expr_backup.push(( + base_expr.span, + adt_ty, + fresh_base_ty, + )); + } fru_tys } else { // Check the base_expr, regardless of a bad expected adt_ty, so we can get diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6034e33662ee4..9a9893e57ae9f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -606,11 +606,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub(in super::super) fn resolve_base_expr(&self) { - for (_, (base_ty, adt_ty)) in self.typeck_results.borrow().base_expr_backup().iter() { + for (span, adt_ty, base_ty) in self.typeck_results.borrow().base_expr_backup.iter() { let base_ty = self.resolve_vars_if_possible(*base_ty); - if base_ty.has_infer_types() { - if let Some(mut err) = - self.demand_base_struct(&self.misc(DUMMY_SP), *adt_ty, base_ty) + if base_ty.needs_infer() { + if let Some(mut err) = self.demand_base_struct(&self.misc(*span), *adt_ty, base_ty) { err.emit(); } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 624443d9594c9..e18cd8df6cd21 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -341,6 +341,10 @@ fn typeck_with_fallback<'tcx>( fcx.require_type_is_sized(ty, span, code); } + // If the base structs used to creat structs still remain infer generics, + // unify it with the created ones. + fcx.resolve_base_expr(); + fcx.select_all_obligations_or_error(); if !fcx.infcx.is_tainted_by_errors() { diff --git a/compiler/rustc_infer/src/infer/base_struct.rs b/compiler/rustc_infer/src/infer/base_struct.rs index 78836999f12c5..b84e8c4b8449f 100644 --- a/compiler/rustc_infer/src/infer/base_struct.rs +++ b/compiler/rustc_infer/src/infer/base_struct.rs @@ -4,10 +4,8 @@ use crate::infer::{InferOk, InferResult}; use rustc_middle::ty; use rustc_middle::ty::relate::{relate_generic_arg, RelateResult, TypeRelation}; use rustc_middle::ty::{GenericArg, SubstsRef, Ty}; -use rustc_span::def_id::DefId; use std::iter; - pub fn base_struct<'a, 'tcx>(at: At<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, ()> { let trace = ToTrace::to_trace(at.infcx.tcx, at.cause, true, a, b); at.infcx.commit_if_ok(|_| { @@ -18,10 +16,14 @@ pub fn base_struct<'a, 'tcx>(at: At<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> Infe }) } -pub fn base_struct_tys<'tcx>(sub: &mut Sub<'_, '_, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { +pub fn base_struct_tys<'tcx>( + sub: &mut Sub<'_, '_, 'tcx>, + a: Ty<'tcx>, + b: Ty<'tcx>, +) -> RelateResult<'tcx, ()> { match (a.kind(), b.kind()) { (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) if a_def == b_def => { - base_struct_substs(sub, a_def.did(), a_substs, b_substs)?; + base_struct_substs(sub, a, a_substs, b_substs)?; Ok(()) } _ => bug!("not adt ty: {:?} and {:?}", a, b), @@ -30,24 +32,16 @@ pub fn base_struct_tys<'tcx>(sub: &mut Sub<'_, '_, 'tcx>, a: Ty<'tcx>, b: Ty<'tc fn base_struct_substs<'tcx>( sub: &mut Sub<'_, '_, 'tcx>, - item_def_id: DefId, + adt_ty: Ty<'tcx>, a_subst: SubstsRef<'tcx>, b_subst: SubstsRef<'tcx>, ) -> RelateResult<'tcx, ()> { - debug!( - "relate_item_substs(item_def_id={:?}, a_subst={:?}, b_subst={:?})", - item_def_id, a_subst, b_subst - ); - let tcx = sub.tcx(); - let variances = tcx.variances_of(item_def_id); + let variances = tcx.variances_of(adt_ty.ty_adt_def().expect("not a adt ty!").did()); - let mut cached_ty = None; iter::zip(a_subst, b_subst).enumerate().for_each(|(i, (a, b))| { - let cached_ty = *cached_ty - .get_or_insert_with(|| tcx.bound_type_of(item_def_id).subst(tcx, a_subst)); let _arg: RelateResult<'tcx, GenericArg<'tcx>> = - relate_generic_arg(sub, variances, cached_ty, a, b, i).or_else(|_| Ok(b)); + relate_generic_arg(sub, variances, adt_ty, a, b, i).or_else(|_| Ok(b)); }); Ok(()) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 70e85b1120c8e..d1e4b8c43af70 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -50,6 +50,7 @@ use self::region_constraints::{ use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; pub mod at; +pub mod base_struct; pub mod canonical; mod combine; mod equate; @@ -71,7 +72,6 @@ pub mod resolve; mod sub; pub mod type_variable; mod undo_log; -pub mod base_struct; #[must_use] #[derive(Debug)] diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index 469e8d68aac49..97354ba5d1bd1 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -11,7 +11,8 @@ use std::mem; /// Ensures `a` is made a subtype of `b`. Returns `a` on success. pub struct Sub<'combine, 'a, 'tcx> { - fields: &'combine mut CombineFields<'a, 'tcx>, a_is_expected: bool, + fields: &'combine mut CombineFields<'a, 'tcx>, + a_is_expected: bool, } impl<'combine, 'infcx, 'tcx> Sub<'combine, 'infcx, 'tcx> { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 3ac64152d3d49..83a63526aeb37 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -515,8 +515,6 @@ pub struct TypeckResults<'tcx> { /// MIR construction and hence is not serialized to metadata. fru_field_types: ItemLocalMap>>, - base_expr_backup: ItemLocalMap<(Ty<'tcx>, Ty<'tcx>)>, - /// For every coercion cast we add the HIR node ID of the cast /// expression to this set. coercion_casts: ItemLocalSet, @@ -583,6 +581,10 @@ pub struct TypeckResults<'tcx> { /// Contains the data for evaluating the effect of feature `capture_disjoint_fields` /// on closure size. pub closure_size_eval: FxHashMap>, + + /// Hold the generic parameters that were not fully instantiated when + /// creating a struct from a base struct. + pub base_expr_backup: Vec<(Span, Ty<'tcx>, Ty<'tcx>)>, } impl<'tcx> TypeckResults<'tcx> { @@ -840,14 +842,6 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.fru_field_types } } - pub fn base_expr_backup(&self) -> LocalTableInContext<'_, (Ty<'tcx>, Ty<'tcx>)> { - LocalTableInContext { hir_owner: self.hir_owner, data: &self.base_expr_backup } - } - - pub fn base_expr_backup_mut(&mut self) -> LocalTableInContextMut<'_, (Ty<'tcx>, Ty<'tcx>)> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.base_expr_backup } - } - pub fn is_coercion_cast(&self, hir_id: hir::HirId) -> bool { validate_hir_id_for_typeck_results(self.hir_owner, hir_id); self.coercion_casts.contains(&hir_id.local_id) diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-101970.rs b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-101970.rs new file mode 100644 index 0000000000000..4327e21d32313 --- /dev/null +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-101970.rs @@ -0,0 +1,40 @@ +#![feature(type_changing_struct_update)] +#![allow(incomplete_features)] + +#[derive(Default)] +pub struct Foo { + pub t: T, + pub v: P +} + +impl Foo { + pub fn new() -> Self { + Foo { t: T::default(), v: P::default() } + } + + pub fn d(t: T) -> Self { + Foo { t, ..Default::default() } + } + + pub fn o(t: T) -> Self { + let d: Foo<_, _> = Foo::new(); + Foo { t, ..d } + } + + pub fn o2(t: T, v: P2) -> (Foo, Foo) { + let d = Default::default(); + let foo1 = Foo { t, ..d }; + let foo2 = Foo { v, ..d }; + (foo1, foo2) + } + + pub fn o3(t1: T, t2: T2) -> (Foo, Foo) { + let d = Default::default(); + let foo1 = Foo { t: t1, ..d }; + let foo2 = Foo { t: t2, ..d }; + //~^ ERROR mismatched types [E0308] + (foo1, foo2) + } +} + +fn main() {} diff --git a/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-101970.stderr b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-101970.stderr new file mode 100644 index 0000000000000..73146052d79e0 --- /dev/null +++ b/src/test/ui/rfcs/rfc-2528-type-changing-struct-update/issue-101970.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/issue-101970.rs:34:35 + | +LL | impl Foo { + | - found type parameter +... +LL | pub fn o3(t1: T, t2: T2) -> (Foo, Foo) { + | -- expected type parameter +... +LL | let foo2 = Foo { t: t2, ..d }; + | ^ expected type parameter `T2`, found type parameter `T` + | + = note: expected type parameter `T2` + found type parameter `T` + = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 61fd9d5577d37ca4c3404de5e76ef21b7d59bbef Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Fri, 11 Nov 2022 12:28:20 +0800 Subject: [PATCH 4/4] enable the feature by default --- compiler/rustc_hir_typeck/src/expr.rs | 2 +- .../feature-gate.rs | 5 ++-- .../feature-gate.stderr | 23 ++++--------------- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d69eb4d513d7d..c596285e71a38 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1642,7 +1642,7 @@ 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 fru_tys = if self.tcx.features().type_changing_struct_update || true { 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()); 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 1e8b99ba5647a..9f37011dd9e5f 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 @@ -18,10 +18,9 @@ fn update_to_state2() { common_field2: 2, }; let m2: Machine = Machine { - state: State2, + state: State1, + //~^ ERROR mismatched types [E0308] ..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 2217b8c049863..3965cbdf0d680 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,22 +1,9 @@ -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:22:11 - | -LL | ..m1 - | ^^ expected struct `State2`, found struct `State1` + --> $DIR/feature-gate.rs:21:16 | - = note: expected struct `Machine` - found struct `Machine` +LL | state: State1, + | ^^^^^^ expected struct `State2`, found struct `State1` -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0308, E0658. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0308`.