about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-06-02 16:02:06 -0700
committerGitHub <noreply@github.com>2023-06-02 16:02:06 -0700
commite4a6b242450aa6040d903ce021bc859b98ff84d8 (patch)
tree215c0212879894ccf863fcd683003eb396d72c01
parent5460f92a0f97238e87fef281134b08deb2f587ee (diff)
parent84196f33710e2797a576ddc5241e418b7a4f1f2c (diff)
downloadrust-e4a6b242450aa6040d903ce021bc859b98ff84d8.tar.gz
rust-e4a6b242450aa6040d903ce021bc859b98ff84d8.zip
Rollup merge of #112183 - compiler-errors:new-solver-anon-ct, r=BoxyUwU
Normalize anon consts in new solver

We don't do any of that `expand_abstract_consts` stuff so this isn't sufficient to make GCE work, but it does allow, e.g. `[(); 1]: Default`, to solve.

r? `@BoxyUwU`
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/mod.rs3
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs2
-rw-r--r--compiler/rustc_middle/src/ty/context.rs9
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs24
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs13
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs71
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs4
-rw-r--r--tests/ui/traits/new-solver/array-default.rs8
-rw-r--r--tests/ui/traits/new-solver/structural-resolve-field.rs26
-rw-r--r--tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.fails.stderr18
-rw-r--r--tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.rs22
13 files changed, 134 insertions, 70 deletions
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 32a7e8af862..e92a0dcc1fb 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -50,7 +50,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 // We do not allow generic parameters in anon consts if we are inside
                 // of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
                 None
-            } else if tcx.lazy_normalization() {
+            } else if tcx.features().generic_const_exprs {
                 let parent_node = tcx.hir().get_parent(hir_id);
                 if let Node::Variant(Variant { disr_expr: Some(constant), .. }) = parent_node
                     && constant.hir_id == hir_id
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 70d950eddd8..dcb57902928 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -463,7 +463,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
             }
         }
     } else {
-        if matches!(def_kind, DefKind::AnonConst) && tcx.lazy_normalization() {
+        if matches!(def_kind, DefKind::AnonConst) && tcx.features().generic_const_exprs {
             let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
             let parent_def_id = tcx.hir().get_parent_item(hir_id);
 
diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs
index 2106d6ff07d..fdbb890ce3d 100644
--- a/compiler/rustc_hir_analysis/src/outlives/mod.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs
@@ -20,7 +20,8 @@ pub fn provide(providers: &mut Providers) {
 fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clause<'_>, Span)] {
     let id = tcx.hir().local_def_id_to_hir_id(item_def_id);
 
-    if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization()
+    if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst)
+        && tcx.features().generic_const_exprs
     {
         if tcx.hir().opt_const_param_default_param_def_id(id).is_some() {
             // In `generics_of` we set the generics' parent to be our parent's parent which means that
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index b6b935de68c..ed532aa2e8b 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -227,7 +227,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 return self.unify_const_variable(vid, a, relation.param_env());
             }
             (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
-                if self.tcx.lazy_normalization() =>
+                if self.tcx.features().generic_const_exprs || self.tcx.trait_solver_next() =>
             {
                 relation.register_const_equate_obligation(a, b);
                 return Ok(b);
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index e7335104e61..bf6f21968d7 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1015,15 +1015,6 @@ impl<'tcx> TyCtxt<'tcx> {
         self.query_system.on_disk_cache.as_ref().map_or(Ok(0), |c| c.serialize(self, encoder))
     }
 
-    /// If `true`, we should use lazy normalization for constants, otherwise
-    /// we still evaluate them eagerly.
-    #[inline]
-    pub fn lazy_normalization(self) -> bool {
-        let features = self.features();
-        // Note: We only use lazy normalization for generic const expressions.
-        features.generic_const_exprs
-    }
-
     #[inline]
     pub fn local_crate_exports_generics(self) -> bool {
         debug_assert!(self.sess.opts.share_generics());
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index f91c6727753..bc93b9e99ad 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -322,10 +322,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                 ty::PredicateKind::Ambiguous => {
                     self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
                 }
-                // FIXME: implement these predicates :)
-                ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => {
+                // FIXME: implement this predicate :)
+                ty::PredicateKind::ConstEvaluatable(_) => {
                     self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
                 }
+                ty::PredicateKind::ConstEquate(_, _) => {
+                    bug!("ConstEquate should not be emitted when `-Ztrait-solver=next` is active")
+                }
                 ty::PredicateKind::TypeWellFormedFromEnv(..) => {
                     bug!("TypeWellFormedFromEnv is only used for Chalk")
                 }
@@ -772,4 +775,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
         values
     }
+
+    // Try to evaluate a const, or return `None` if the const is too generic.
+    // This doesn't mean the const isn't evaluatable, though, and should be treated
+    // as an ambiguity rather than no-solution.
+    pub(super) fn try_const_eval_resolve(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        unevaluated: ty::UnevaluatedConst<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Option<ty::Const<'tcx>> {
+        use rustc_middle::mir::interpret::ErrorHandled;
+        match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) {
+            Ok(ct) => Some(ct),
+            Err(ErrorHandled::Reported(e)) => Some(self.tcx().const_error(ty, e.into())),
+            Err(ErrorHandled::TooGeneric) => None,
+        }
+    }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 4a403196c7e..212327448c8 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -118,16 +118,6 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
                                         TypeError::Sorts(expected_found),
                                     )
                                 }
-                                ty::PredicateKind::ConstEquate(a, b) => {
-                                    let (a, b) = infcx.instantiate_binder_with_placeholders(
-                                        goal.predicate.kind().rebind((a, b)),
-                                    );
-                                    let expected_found = ExpectedFound::new(true, a, b);
-                                    FulfillmentErrorCode::CodeConstEquateError(
-                                        expected_found,
-                                        TypeError::ConstMismatch(expected_found),
-                                    )
-                                }
                                 ty::PredicateKind::Clause(_)
                                 | ty::PredicateKind::WellFormed(_)
                                 | ty::PredicateKind::ObjectSafe(_)
@@ -138,7 +128,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
                                         SelectionError::Unimplemented,
                                     )
                                 }
-                                ty::PredicateKind::TypeWellFormedFromEnv(_) => {
+                                ty::PredicateKind::ConstEquate(..)
+                                | ty::PredicateKind::TypeWellFormedFromEnv(_) => {
                                     bug!("unexpected goal: {goal:?}")
                                 }
                             },
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index 7d7dfa2c837..23601f668ff 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -22,25 +22,65 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         &mut self,
         goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
     ) -> QueryResult<'tcx> {
-        match goal.predicate.projection_ty.kind(self.tcx()) {
-            ty::AliasKind::Projection => {
+        let def_id = goal.predicate.def_id();
+        match self.tcx().def_kind(def_id) {
+            DefKind::AssocTy | DefKind::AssocConst => {
                 // To only compute normalization once for each projection we only
-                // normalize if the expected term is an unconstrained inference variable.
+                // assemble normalization candidates if the expected term is an
+                // unconstrained inference variable.
+                //
+                // Why: For better cache hits, since if we have an unconstrained RHS then
+                // there are only as many cache keys as there are (canonicalized) alias
+                // types in each normalizes-to goal. This also weakens inference in a
+                // forwards-compatible way so we don't use the value of the RHS term to
+                // affect candidate assembly for projections.
                 //
                 // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
                 // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
                 // `U` and equate it with `u32`. This means that we don't need a separate
-                // projection cache in the solver.
+                // projection cache in the solver, since we're piggybacking off of regular
+                // goal caching.
                 if self.term_is_fully_unconstrained(goal) {
-                    let candidates = self.assemble_and_evaluate_candidates(goal);
-                    self.merge_candidates(candidates)
+                    match self.tcx().associated_item(def_id).container {
+                        ty::AssocItemContainer::TraitContainer => {
+                            let candidates = self.assemble_and_evaluate_candidates(goal);
+                            self.merge_candidates(candidates)
+                        }
+                        ty::AssocItemContainer::ImplContainer => {
+                            bug!("IATs not supported here yet")
+                        }
+                    }
                 } else {
                     self.set_normalizes_to_hack_goal(goal);
                     self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
                 }
             }
-            ty::AliasKind::Opaque => self.normalize_opaque_type(goal),
-            ty::AliasKind::Inherent => bug!("IATs not supported here yet"),
+            DefKind::AnonConst => self.normalize_anon_const(goal),
+            DefKind::OpaqueTy => self.normalize_opaque_type(goal),
+            kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)),
+        }
+    }
+
+    #[instrument(level = "debug", skip(self), ret)]
+    fn normalize_anon_const(
+        &mut self,
+        goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        if let Some(normalized_const) = self.try_const_eval_resolve(
+            goal.param_env,
+            ty::UnevaluatedConst::new(
+                goal.predicate.projection_ty.def_id,
+                goal.predicate.projection_ty.substs,
+            ),
+            self.tcx()
+                .type_of(goal.predicate.projection_ty.def_id)
+                .no_bound_vars()
+                .expect("const ty should not rely on other generics"),
+        ) {
+            self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
+            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        } else {
+            self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
         }
     }
 }
@@ -173,17 +213,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
             );
 
             // Finally we construct the actual value of the associated type.
-            let is_const = matches!(tcx.def_kind(assoc_def.item.def_id), DefKind::AssocConst);
-            let ty = tcx.type_of(assoc_def.item.def_id);
-            let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
-                let identity_substs =
-                    ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id);
-                let did = assoc_def.item.def_id;
-                let kind =
-                    ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs));
-                ty.map_bound(|ty| tcx.mk_const(kind, ty).into())
-            } else {
-                ty.map_bound(|ty| ty.into())
+            let term = match assoc_def.item.kind {
+                ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()),
+                ty::AssocKind::Const => bug!("associated const projection is not supported yet"),
+                ty::AssocKind::Fn => unreachable!("we should never project to a fn"),
             };
 
             ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 65af0bb1c4e..563cc257e03 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -672,7 +672,9 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
     #[instrument(skip(self), level = "debug")]
     fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
         let tcx = self.selcx.tcx();
-        if tcx.lazy_normalization() || !needs_normalization(&constant, self.param_env.reveal()) {
+        if tcx.features().generic_const_exprs
+            || !needs_normalization(&constant, self.param_env.reveal())
+        {
             constant
         } else {
             let constant = constant.super_fold_with(self);
diff --git a/tests/ui/traits/new-solver/array-default.rs b/tests/ui/traits/new-solver/array-default.rs
new file mode 100644
index 00000000000..5077137b09b
--- /dev/null
+++ b/tests/ui/traits/new-solver/array-default.rs
@@ -0,0 +1,8 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+fn has_default<const N: usize>() where [(); N]: Default {}
+
+fn main() {
+    has_default::<1>();
+}
diff --git a/tests/ui/traits/new-solver/structural-resolve-field.rs b/tests/ui/traits/new-solver/structural-resolve-field.rs
index c492d927696..01899c9ad64 100644
--- a/tests/ui/traits/new-solver/structural-resolve-field.rs
+++ b/tests/ui/traits/new-solver/structural-resolve-field.rs
@@ -1,35 +1,13 @@
 // compile-flags: -Ztrait-solver=next
 // check-pass
 
+#[derive(Default)]
 struct Foo {
     x: i32,
 }
 
-impl MyDefault for Foo {
-    fn my_default() -> Self {
-        Self {
-            x: 0,
-        }
-    }
-}
-
-trait MyDefault {
-    fn my_default() -> Self;
-}
-
-impl MyDefault for [Foo; 0]  {
-    fn my_default() -> Self {
-        []
-    }
-}
-impl MyDefault for [Foo; 1] {
-    fn my_default() -> Self {
-        [Foo::my_default(); 1]
-    }
-}
-
 fn main() {
-    let mut xs = <[Foo; 1]>::my_default();
+    let mut xs = <[Foo; 1]>::default();
     xs[0].x = 1;
     (&mut xs[0]).x = 2;
 }
diff --git a/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.fails.stderr b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.fails.stderr
new file mode 100644
index 00000000000..072ac32a5de
--- /dev/null
+++ b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.fails.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `(): Trait<1>` is not satisfied
+  --> $DIR/unevaluated-const-impl-trait-ref.rs:20:13
+   |
+LL |     needs::<1>();
+   |             ^ the trait `Trait<1>` is not implemented for `()`
+   |
+   = help: the following other types implement trait `Trait<N>`:
+             <() as Trait<0>>
+             <() as Trait<2>>
+note: required by a bound in `needs`
+  --> $DIR/unevaluated-const-impl-trait-ref.rs:10:38
+   |
+LL | fn needs<const N: usize>() where (): Trait<N> {}
+   |                                      ^^^^^^^^ required by this bound in `needs`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.rs b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.rs
new file mode 100644
index 00000000000..26c595bc974
--- /dev/null
+++ b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.rs
@@ -0,0 +1,22 @@
+// compile-flags: -Ztrait-solver=next
+// revisions: works fails
+//[works] check-pass
+
+trait Trait<const N: usize> {}
+
+impl Trait<{ 1 - 1 }> for () {}
+impl Trait<{ 1 + 1 }> for () {}
+
+fn needs<const N: usize>() where (): Trait<N> {}
+
+#[cfg(works)]
+fn main() {
+    needs::<0>();
+    needs::<2>();
+}
+
+#[cfg(fails)]
+fn main() {
+    needs::<1>();
+    //[fails]~^ ERROR the trait bound `(): Trait<1>` is not satisfied
+}