diff options
| author | León Orell Valerian Liehr <me@fmease.dev> | 2023-07-02 18:48:43 +0200 |
|---|---|---|
| committer | León Orell Valerian Liehr <me@fmease.dev> | 2023-07-28 22:21:41 +0200 |
| commit | 8c390286e4de59ddfff6bd7c7fdea10827f89b8d (patch) | |
| tree | 4fe1db555addbf47d98c5cf9e801eaa38632deb5 | |
| parent | da17134be044c1d317cb4eccb56e9498e46d8bee (diff) | |
| download | rust-8c390286e4de59ddfff6bd7c7fdea10827f89b8d.tar.gz rust-8c390286e4de59ddfff6bd7c7fdea10827f89b8d.zip | |
Type-check generic const items
4 files changed, 122 insertions, 103 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 919092ecb16..75c45868b90 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -76,7 +76,7 @@ fn check_method_is_structurally_compatible<'tcx>( Ok(()) } -/// This function is best explained by example. Consider a trait with it's implementation: +/// This function is best explained by example. Consider a trait with its implementation: /// /// ```rust /// trait Trait<'t, T> { @@ -120,7 +120,7 @@ fn check_method_is_structurally_compatible<'tcx>( /// types: /// /// ```rust,ignore (pseudo-Rust) -/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo +/// <'b> fn(t: &'i0 U0, m: &'b N0) -> Foo /// ``` /// /// We now want to extract and substitute the type of the *trait* @@ -137,7 +137,7 @@ fn check_method_is_structurally_compatible<'tcx>( /// Applying this to the trait method type yields: /// /// ```rust,ignore (pseudo-Rust) -/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo +/// <'a> fn(t: &'i0 U0, m: &'a N0) -> Foo /// ``` /// /// This type is also the same but the name of the bound region (`'a` @@ -258,8 +258,6 @@ fn compare_method_predicate_entailment<'tcx>( // type. // Compute placeholder form of impl and trait method tys. - let tcx = infcx.tcx; - let mut wf_tys = FxIndexSet::default(); let unnormalized_impl_sig = infcx.instantiate_binder_with_fresh_vars( @@ -1668,19 +1666,19 @@ fn compare_synthetic_generics<'tcx>( /// ```rust,ignore (pseudo-Rust) /// trait Foo { /// fn foo<const N: u8>(); -/// type bar<const N: u8>; +/// type Bar<const N: u8>; /// fn baz<const N: u32>(); -/// type blah<T>; +/// type Blah<T>; /// } /// /// impl Foo for () { /// fn foo<const N: u64>() {} /// //~^ error -/// type bar<const N: u64> {} +/// type Bar<const N: u64> = (); /// //~^ error /// fn baz<T>() {} /// //~^ error -/// type blah<const N: i64> = u32; +/// type Blah<const N: i64> = u32; /// //~^ error /// } /// ``` @@ -1769,36 +1767,82 @@ pub(super) fn compare_impl_const_raw( let trait_const_item = tcx.associated_item(trait_const_item_def); let impl_trait_ref = tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().instantiate_identity(); - debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref); - let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id()); + debug!("compare_impl_const(impl_trait_ref={:?})", impl_trait_ref); - let infcx = tcx.infer_ctxt().build(); - let param_env = tcx.param_env(impl_const_item_def.to_def_id()); - let ocx = ObligationCtxt::new(&infcx); + compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?; + compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?; + compare_const_predicate_entailment(tcx, impl_const_item, trait_const_item, impl_trait_ref) +} + +/// The equivalent of [compare_method_predicate_entailment], but for associated constants +/// instead of associated functions. +// FIXME(generic_const_items): If possible extract the common parts of `compare_{type,const}_predicate_entailment`. +fn compare_const_predicate_entailment<'tcx>( + tcx: TyCtxt<'tcx>, + impl_ct: ty::AssocItem, + trait_ct: ty::AssocItem, + impl_trait_ref: ty::TraitRef<'tcx>, +) -> Result<(), ErrorGuaranteed> { + let impl_ct_def_id = impl_ct.def_id.expect_local(); + let impl_ct_span = tcx.def_span(impl_ct_def_id); // The below is for the most part highly similar to the procedure // for methods above. It is simpler in many respects, especially // because we shouldn't really have to deal with lifetimes or // predicates. In fact some of this should probably be put into // shared functions because of DRY violations... - let trait_to_impl_args = impl_trait_ref.args; + let impl_args = GenericArgs::identity_for_item(tcx, impl_ct.def_id); + let trait_to_impl_args = + impl_args.rebase_onto(tcx, impl_ct.container_id(tcx), impl_trait_ref.args); // Create a parameter environment that represents the implementation's // method. // Compute placeholder form of impl and trait const tys. - let impl_ty = tcx.type_of(impl_const_item_def.to_def_id()).instantiate_identity(); - let trait_ty = tcx.type_of(trait_const_item_def).instantiate(tcx, trait_to_impl_args); - let mut cause = ObligationCause::new( - impl_c_span, - impl_const_item_def, - ObligationCauseCode::CompareImplItemObligation { - impl_item_def_id: impl_const_item_def, - trait_item_def_id: trait_const_item_def, - kind: impl_const_item.kind, - }, + let impl_ty = tcx.type_of(impl_ct_def_id).instantiate_identity(); + + let trait_ty = tcx.type_of(trait_ct.def_id).instantiate(tcx, trait_to_impl_args); + let code = ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_ct_def_id, + trait_item_def_id: trait_ct.def_id, + kind: impl_ct.kind, + }; + let mut cause = ObligationCause::new(impl_ct_span, impl_ct_def_id, code.clone()); + + let impl_ct_predicates = tcx.predicates_of(impl_ct.def_id); + let trait_ct_predicates = tcx.predicates_of(trait_ct.def_id); + + check_region_bounds_on_impl_item(tcx, impl_ct, trait_ct, false)?; + + // The predicates declared by the impl definition, the trait and the + // associated const in the trait are assumed. + let impl_predicates = tcx.predicates_of(impl_ct_predicates.parent.unwrap()); + let mut hybrid_preds = impl_predicates.instantiate_identity(tcx); + hybrid_preds.predicates.extend( + trait_ct_predicates + .instantiate_own(tcx, trait_to_impl_args) + .map(|(predicate, _)| predicate), + ); + + let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing); + let param_env = traits::normalize_param_env_or_error( + tcx, + param_env, + ObligationCause::misc(impl_ct_span, impl_ct_def_id), ); + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + + let impl_ct_own_bounds = impl_ct_predicates.instantiate_own(tcx, impl_args); + for (predicate, span) in impl_ct_own_bounds { + let cause = ObligationCause::misc(span, impl_ct_def_id); + let predicate = ocx.normalize(&cause, param_env, predicate); + + let cause = ObligationCause::new(span, impl_ct_def_id, code.clone()); + ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate)); + } + // There is no "body" here, so just pass dummy id. let impl_ty = ocx.normalize(&cause, param_env, impl_ty); @@ -1817,7 +1861,7 @@ pub(super) fn compare_impl_const_raw( ); // Locate the Span containing just the type of the offending impl - let (ty, _) = tcx.hir().expect_impl_item(impl_const_item_def).expect_const(); + let (ty, _) = tcx.hir().expect_impl_item(impl_ct_def_id).expect_const(); cause.span = ty.span; let mut diag = struct_span_err!( @@ -1825,12 +1869,12 @@ pub(super) fn compare_impl_const_raw( cause.span, E0326, "implemented const `{}` has an incompatible type for trait", - trait_const_item.name + trait_ct.name ); - let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| { + let trait_c_span = trait_ct.def_id.as_local().map(|trait_ct_def_id| { // Add a label to the Span containing just the type of the const - let (ty, _) = tcx.hir().expect_trait_item(trait_c_def_id).expect_const(); + let (ty, _) = tcx.hir().expect_trait_item(trait_ct_def_id).expect_const(); ty.span }); @@ -1857,7 +1901,7 @@ pub(super) fn compare_impl_const_raw( } let outlives_env = OutlivesEnvironment::new(param_env); - ocx.resolve_regions_and_report_errors(impl_const_item_def, &outlives_env) + ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env) } pub(super) fn compare_impl_ty<'tcx>( @@ -1899,7 +1943,7 @@ fn compare_type_predicate_entailment<'tcx>( return Ok(()); } - // This `HirId` should be used for the `body_id` field on each + // This `DefId` should be used for the `body_id` field on each // `ObligationCause` (and the `FnCtxt`). This is what // `regionck_item` expects. let impl_ty_def_id = impl_ty.def_id.expect_local(); @@ -1918,7 +1962,7 @@ fn compare_type_predicate_entailment<'tcx>( debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds); let impl_ty_span = tcx.def_span(impl_ty_def_id); - let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_def_id); + let normalize_cause = ObligationCause::misc(impl_ty_span, impl_ty_def_id); let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing); let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); let infcx = tcx.infer_ctxt().build(); @@ -1963,7 +2007,7 @@ fn compare_type_predicate_entailment<'tcx>( /// /// trait X { type Y: Copy } impl X for T { type Y = S; } /// -/// We are able to normalize `<T as X>::U` to `S`, and so when we check the +/// We are able to normalize `<T as X>::Y` to `S`, and so when we check the /// impl is well-formed we have to prove `S: Copy`. /// /// For default associated types the normalization is not possible (the value diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index edcb9527fe2..6e1762c54f2 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -209,6 +209,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { | ItemKind::Struct(..) | ItemKind::OpaqueTy(..) | ItemKind::Union(..) => (None, Defaults::Allowed), + ItemKind::Const(..) => (None, Defaults::Deny), _ => (None, Defaults::FutureCompatDisallowed), } } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 979b101e7fe..ab3b2dde078 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -156,6 +156,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } ItemKind::Fn(.., generics, _) | ItemKind::TyAlias(_, generics) + | ItemKind::Const(_, generics, _) | ItemKind::Enum(_, generics) | ItemKind::Struct(_, generics) | ItemKind::Union(_, generics) => generics, @@ -762,6 +763,7 @@ pub(super) fn type_param_predicates( ItemKind::Fn(.., generics, _) | ItemKind::Impl(&hir::Impl { generics, .. }) | ItemKind::TyAlias(_, generics) + | ItemKind::Const(_, generics, _) | ItemKind::OpaqueTy(&OpaqueTy { generics, origin: hir::OpaqueTyOrigin::TyAlias { .. }, diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index eb93817e823..3cc6f574aec 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -518,7 +518,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { | hir::ItemKind::Mod(..) | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::Static(..) - | hir::ItemKind::Const(..) | hir::ItemKind::GlobalAsm(..) => { // These sorts of items have no lifetime parameters at all. intravisit::walk_item(self, item); @@ -583,6 +582,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }) } hir::ItemKind::TyAlias(_, generics) + | hir::ItemKind::Const(_, generics, _) | hir::ItemKind::Enum(_, generics) | hir::ItemKind::Struct(_, generics) | hir::ItemKind::Union(_, generics) @@ -590,21 +590,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { | hir::ItemKind::TraitAlias(generics, ..) | hir::ItemKind::Impl(&hir::Impl { generics, .. }) => { // These kinds of items have only early-bound lifetime parameters. - let bound_vars = generics.params.iter().map(ResolvedArg::early).collect(); - self.record_late_bound_vars(item.hir_id(), vec![]); - let scope = Scope::Binder { - hir_id: item.hir_id(), - bound_vars, - scope_type: BinderScopeType::Normal, - s: self.scope, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - intravisit::walk_item(this, item); - }); - }); + self.visit_early(item.hir_id(), generics, |this| intravisit::walk_item(this, item)); } } } @@ -777,39 +763,24 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { use self::hir::TraitItemKind::*; match trait_item.kind { Fn(_, _) => { - self.visit_early_late(trait_item.hir_id(), &trait_item.generics, |this| { + self.visit_early_late(trait_item.hir_id(), trait_item.generics, |this| { intravisit::walk_trait_item(this, trait_item) }); } Type(bounds, ty) => { - let generics = &trait_item.generics; - let bound_vars = generics.params.iter().map(ResolvedArg::early).collect(); - self.record_late_bound_vars(trait_item.hir_id(), vec![]); - let scope = Scope::Binder { - hir_id: trait_item.hir_id(), - bound_vars, - s: self.scope, - scope_type: BinderScopeType::Normal, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - this.visit_generics(generics); - for bound in bounds { - this.visit_param_bound(bound); - } - if let Some(ty) = ty { - this.visit_ty(ty); - } - }) - }); - } - Const(_, _) => { - // Only methods and types support generics. - assert!(trait_item.generics.params.is_empty()); - intravisit::walk_trait_item(self, trait_item); + self.visit_early(trait_item.hir_id(), trait_item.generics, |this| { + this.visit_generics(&trait_item.generics); + for bound in bounds { + this.visit_param_bound(bound); + } + if let Some(ty) = ty { + this.visit_ty(ty); + } + }) } + Const(_, _) => self.visit_early(trait_item.hir_id(), trait_item.generics, |this| { + intravisit::walk_trait_item(this, trait_item) + }), } } @@ -817,34 +788,16 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { use self::hir::ImplItemKind::*; match impl_item.kind { - Fn(..) => self.visit_early_late(impl_item.hir_id(), &impl_item.generics, |this| { + Fn(..) => self.visit_early_late(impl_item.hir_id(), impl_item.generics, |this| { + intravisit::walk_impl_item(this, impl_item) + }), + Type(ty) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| { + this.visit_generics(impl_item.generics); + this.visit_ty(ty); + }), + Const(_, _) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| { intravisit::walk_impl_item(this, impl_item) }), - Type(ty) => { - let generics = &impl_item.generics; - let bound_vars: FxIndexMap<LocalDefId, ResolvedArg> = - generics.params.iter().map(ResolvedArg::early).collect(); - self.record_late_bound_vars(impl_item.hir_id(), vec![]); - let scope = Scope::Binder { - hir_id: impl_item.hir_id(), - bound_vars, - s: self.scope, - scope_type: BinderScopeType::Normal, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - this.visit_generics(generics); - this.visit_ty(ty); - }) - }); - } - Const(_, _) => { - // Only methods and types support generics. - assert!(impl_item.generics.params.is_empty()); - intravisit::walk_impl_item(self, impl_item); - } } } @@ -1180,6 +1133,25 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { self.with(scope, walk); } + fn visit_early<F>(&mut self, hir_id: hir::HirId, generics: &'tcx hir::Generics<'tcx>, walk: F) + where + F: for<'b, 'c> FnOnce(&'b mut BoundVarContext<'c, 'tcx>), + { + let bound_vars = generics.params.iter().map(ResolvedArg::early).collect(); + self.record_late_bound_vars(hir_id, vec![]); + let scope = Scope::Binder { + hir_id, + bound_vars, + s: self.scope, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + self.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, walk) + }); + } + #[instrument(level = "debug", skip(self))] fn resolve_lifetime_ref( &mut self, |
