about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2023-07-02 18:48:43 +0200
committerLeón Orell Valerian Liehr <me@fmease.dev>2023-07-28 22:21:41 +0200
commit8c390286e4de59ddfff6bd7c7fdea10827f89b8d (patch)
tree4fe1db555addbf47d98c5cf9e801eaa38632deb5
parentda17134be044c1d317cb4eccb56e9498e46d8bee (diff)
downloadrust-8c390286e4de59ddfff6bd7c7fdea10827f89b8d.tar.gz
rust-8c390286e4de59ddfff6bd7c7fdea10827f89b8d.zip
Type-check generic const items
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs110
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs112
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,