summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_analysis/src/impl_wf_check
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:25 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:25 +0000
commit5363f350887b1e5b5dd21a86f88c8af9d7fea6da (patch)
tree35ca005eb6e0e9a1ba3bb5dbc033209ad445dc17 /compiler/rustc_hir_analysis/src/impl_wf_check
parentAdding debian version 1.66.0+dfsg1-1. (diff)
downloadrustc-5363f350887b1e5b5dd21a86f88c8af9d7fea6da.tar.xz
rustc-5363f350887b1e5b5dd21a86f88c8af9d7fea6da.zip
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_hir_analysis/src/impl_wf_check')
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs149
1 files changed, 120 insertions, 29 deletions
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<T: Bound> Tr for T { }
+/// impl<T: ~const Bound + Specialize> 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<T: ~const Bound> const Tr for T { }
+/// impl<T: Bound + Specialize> 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<TraitSpecializationKind> {
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,
}
}