From 4547b622d8d29df964fa2914213088b148c498fc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:18:32 +0200 Subject: Merging upstream version 1.67.1+dfsg1. Signed-off-by: Daniel Baumann --- .../src/impl_wf_check/min_specialization.rs | 149 +++++++++++++++++---- 1 file changed, 120 insertions(+), 29 deletions(-) (limited to 'compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs') diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index e806e9487..fd8e8ed7b 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -69,6 +69,7 @@ use crate::constrained_generic_params as cgp; use crate::errors::SubstsOnOverriddenImpl; use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::TyCtxtInferExt; @@ -80,6 +81,7 @@ use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt}; +use tracing::instrument; pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { if let Some(node) = parent_specialization_node(tcx, impl_def_id) { @@ -103,13 +105,11 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti } /// Check that `impl1` is a sound specialization +#[instrument(level = "debug", skip(tcx))] fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node) { if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) { let impl2_def_id = impl2_node.def_id(); - debug!( - "check_always_applicable(\nimpl1_def_id={:?},\nimpl2_def_id={:?},\nimpl2_substs={:?}\n)", - impl1_def_id, impl2_def_id, impl2_substs - ); + debug!(?impl2_def_id, ?impl2_substs); let parent_substs = if impl2_node.is_from_trait() { impl2_substs.to_vec() @@ -118,12 +118,33 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node }; let span = tcx.def_span(impl1_def_id); + check_constness(tcx, impl1_def_id, impl2_node, span); check_static_lifetimes(tcx, &parent_substs, span); check_duplicate_params(tcx, impl1_substs, &parent_substs, span); check_predicates(tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span); } } +/// Check that the specializing impl `impl1` is at least as const as the base +/// impl `impl2` +fn check_constness(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, span: Span) { + if impl2_node.is_from_trait() { + // This isn't a specialization + return; + } + + let impl1_constness = tcx.constness(impl1_def_id.to_def_id()); + let impl2_constness = tcx.constness(impl2_node.def_id()); + + if let hir::Constness::Const = impl2_constness { + if let hir::Constness::NotConst = impl1_constness { + tcx.sess + .struct_span_err(span, "cannot specialize on const impl with non-const impl") + .emit(); + } + } +} + /// Given a specializing impl `impl1`, and the base impl `impl2`, returns two /// substitutions `(S1, S2)` that equate their trait references. The returned /// types are expressed in terms of the generics of `impl1`. @@ -155,7 +176,7 @@ fn get_impl_substs<'tcx>( let errors = ocx.select_all_or_error(); if !errors.is_empty() { - ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None); return None; } @@ -193,7 +214,9 @@ fn unconstrained_parent_impl_substs<'tcx>( // the functions in `cgp` add the constrained parameters to a list of // unconstrained parameters. for (predicate, _) in impl_generic_predicates.predicates.iter() { - if let ty::PredicateKind::Projection(proj) = predicate.kind().skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = + predicate.kind().skip_binder() + { let projection_ty = proj.projection_ty; let projected_ty = proj.term; @@ -278,15 +301,15 @@ fn check_static_lifetimes<'tcx>( /// Check whether predicates on the specializing impl (`impl1`) are allowed. /// -/// Each predicate `P` must be: +/// Each predicate `P` must be one of: /// -/// * global (not reference any parameters) -/// * `T: Tr` predicate where `Tr` is an always-applicable trait -/// * on the base `impl impl2` -/// * Currently this check is done using syntactic equality, which is -/// conservative but generally sufficient. -/// * a well-formed predicate of a type argument of the trait being implemented, +/// * Global (not reference any parameters). +/// * A `T: Tr` predicate where `Tr` is an always-applicable trait. +/// * Present on the base impl `impl2`. +/// * This check is done using the `trait_predicates_eq` function below. +/// * A well-formed predicate of a type argument of the trait being implemented, /// including the `Self`-type. +#[instrument(level = "debug", skip(tcx))] fn check_predicates<'tcx>( tcx: TyCtxt<'tcx>, impl1_def_id: LocalDefId, @@ -322,10 +345,7 @@ fn check_predicates<'tcx>( .map(|obligation| obligation.predicate) .collect() }; - debug!( - "check_always_applicable(\nimpl1_predicates={:?},\nimpl2_predicates={:?}\n)", - impl1_predicates, impl2_predicates, - ); + debug!(?impl1_predicates, ?impl2_predicates); // Since impls of always applicable traits don't get to assume anything, we // can also assume their supertraits apply. @@ -373,25 +393,90 @@ fn check_predicates<'tcx>( ); for (predicate, span) in impl1_predicates { - if !impl2_predicates.contains(&predicate) { + if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) { check_specialization_on(tcx, predicate, span) } } } +/// Checks if some predicate on the specializing impl (`predicate1`) is the same +/// as some predicate on the base impl (`predicate2`). +/// +/// This basically just checks syntactic equivalence, but is a little more +/// forgiving since we want to equate `T: Tr` with `T: ~const Tr` so this can work: +/// +/// ```ignore (illustrative) +/// #[rustc_specialization_trait] +/// trait Specialize { } +/// +/// impl Tr for T { } +/// impl const Tr for T { } +/// ``` +/// +/// However, we *don't* want to allow the reverse, i.e., when the bound on the +/// specializing impl is not as const as the bound on the base impl: +/// +/// ```ignore (illustrative) +/// impl const Tr for T { } +/// impl const Tr for T { } // should be T: ~const Bound +/// ``` +/// +/// So we make that check in this function and try to raise a helpful error message. +fn trait_predicates_eq<'tcx>( + tcx: TyCtxt<'tcx>, + predicate1: ty::Predicate<'tcx>, + predicate2: ty::Predicate<'tcx>, + span: Span, +) -> bool { + let pred1_kind = predicate1.kind().skip_binder(); + let pred2_kind = predicate2.kind().skip_binder(); + let (trait_pred1, trait_pred2) = match (pred1_kind, pred2_kind) { + ( + ty::PredicateKind::Clause(ty::Clause::Trait(pred1)), + ty::PredicateKind::Clause(ty::Clause::Trait(pred2)), + ) => (pred1, pred2), + // Just use plain syntactic equivalence if either of the predicates aren't + // trait predicates or have bound vars. + _ => return predicate1 == predicate2, + }; + + let predicates_equal_modulo_constness = { + let pred1_unconsted = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred1 }; + let pred2_unconsted = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred2 }; + pred1_unconsted == pred2_unconsted + }; + + if !predicates_equal_modulo_constness { + return false; + } + + // Check that the predicate on the specializing impl is at least as const as + // the one on the base. + match (trait_pred2.constness, trait_pred1.constness) { + (ty::BoundConstness::ConstIfConst, ty::BoundConstness::NotConst) => { + tcx.sess.struct_span_err(span, "missing `~const` qualifier for specialization").emit(); + } + _ => {} + } + + true +} + +#[instrument(level = "debug", skip(tcx))] fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, span: Span) { - debug!("can_specialize_on(predicate = {:?})", predicate); match predicate.kind().skip_binder() { // Global predicates are either always true or always false, so we // are fine to specialize on. _ if predicate.is_global() => (), // We allow specializing on explicitly marked traits with no associated // items. - ty::PredicateKind::Trait(ty::TraitPredicate { + ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { trait_ref, - constness: ty::BoundConstness::NotConst, + constness: _, polarity: _, - }) => { + })) => { if !matches!( trait_predicate_kind(tcx, predicate), Some(TraitSpecializationKind::Marker) @@ -407,7 +492,10 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc .emit(); } } - ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => { + ty::PredicateKind::Clause(ty::Clause::Projection(ty::ProjectionPredicate { + projection_ty, + term, + })) => { tcx.sess .struct_span_err( span, @@ -428,12 +516,14 @@ fn trait_predicate_kind<'tcx>( predicate: ty::Predicate<'tcx>, ) -> Option { match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity: _ }) => { - Some(tcx.trait_def(trait_ref.def_id).specialization_kind) - } - ty::PredicateKind::RegionOutlives(_) - | ty::PredicateKind::TypeOutlives(_) - | ty::PredicateKind::Projection(_) + ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { + trait_ref, + constness: _, + polarity: _, + })) => Some(tcx.trait_def(trait_ref.def_id).specialization_kind), + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_)) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_)) + | ty::PredicateKind::Clause(ty::Clause::Projection(_)) | ty::PredicateKind::WellFormed(_) | ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) @@ -441,6 +531,7 @@ fn trait_predicate_kind<'tcx>( | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } } -- cgit v1.2.3