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` 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> { + 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 `::Assoc == u32` we recursively compute the goal // `exists ::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> = 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> 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() 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`: + <() 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() where (): Trait {} + | ^^^^^^^^ 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 {} + +impl Trait<{ 1 - 1 }> for () {} +impl Trait<{ 1 + 1 }> for () {} + +fn needs() where (): Trait {} + +#[cfg(works)] +fn main() { + needs::<0>(); + needs::<2>(); +} + +#[cfg(fails)] +fn main() { + needs::<1>(); + //[fails]~^ ERROR the trait bound `(): Trait<1>` is not satisfied +}