about summary refs log tree commit diff
path: root/compiler/rustc_hir_analysis/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-07-17 01:57:55 +0000
committerbors <bors@rust-lang.org>2025-07-17 01:57:55 +0000
commit014bd8290f084c714995205a9116e6c035419ae6 (patch)
tree70a98d65e3a93b13c6c896c708c4b66c373733c1 /compiler/rustc_hir_analysis/src
parentfd2eb391d032181459773f3498c17b198513e0d0 (diff)
parente331de149f4fe5ddafe7f4bad13acb1499a6108e (diff)
downloadrust-014bd8290f084c714995205a9116e6c035419ae6.tar.gz
rust-014bd8290f084c714995205a9116e6c035419ae6.zip
Auto merge of #140399 - tiif:unstable_impl, r=lcnr,BoxyUwU
Implement unstable trait impl

This PR allows marking impls of stable trait with stable type as unstable.

## Approach

In std/core, an impl can be marked as unstable by annotating it with ``#[unstable_feature_bound(feat_name)]``. This will add a ``ClauseKind::Unstable_Feature(feat_name)`` to the list of predicates in ``predicates_of`` .

When an unstable impl's function is called, we will first iterate through all the goals in ``param_env`` to check if there is any ``ClauseKind::UnstableFeature(feat_name)`` in ``param_env``.

The existence of ``ClauseKind::Unstable_Feature(feat_name)`` in ``param_env`` means an``#[unstable_feature_bound(feat_name)]`` is present at the call site of the function, so we allow the check to succeed in this case.

If ``ClauseKind::UnstableFeature(feat_name)`` does not exist in ``param_env``, we will still allow the check to succeed for either of the cases below:
1. The feature is enabled through ``#[feature(feat_name)]`` outside of std / core.
2. We are in codegen because we may be monomorphizing a body from an upstream crate which had an unstable feature enabled that the downstream crate do not.

For the rest of the case, it will fail with ambiguity.

## Limitation

In this PR, we do not support:
1. using items that need ``#[unstable_feature_bound]`` within stable APIs
2. annotate main function with ``#[unstable_feature_bound]``
3. annotate ``#[unstable_feature_bound]`` on items other than free function and impl

## Acknowledgement
The design and mentoring are done by `@BoxyUwU`
Diffstat (limited to 'compiler/rustc_hir_analysis/src')
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs14
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs16
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/explicit.rs1
4 files changed, 27 insertions, 5 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 607028f4d9a..14ec82ede1c 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -2212,12 +2212,16 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
         let implied_obligations = traits::elaborate(tcx, predicates_with_span);
 
         for (pred, obligation_span) in implied_obligations {
-            // We lower empty bounds like `Vec<dyn Copy>:` as
-            // `WellFormed(Vec<dyn Copy>)`, which will later get checked by
-            // regular WF checking
-            if let ty::ClauseKind::WellFormed(..) = pred.kind().skip_binder() {
-                continue;
+            match pred.kind().skip_binder() {
+                // We lower empty bounds like `Vec<dyn Copy>:` as
+                // `WellFormed(Vec<dyn Copy>)`, which will later get checked by
+                // regular WF checking
+                ty::ClauseKind::WellFormed(..)
+                // Unstable feature goals cannot be proven in an empty environment so skip them
+                | ty::ClauseKind::UnstableFeature(..) => continue,
+                _ => {}
             }
+
             // Match the existing behavior.
             if pred.is_global() && !pred.has_type_flags(TypeFlags::HAS_BINDER_VARS) {
                 let pred = self.normalize(span, None, pred);
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index a93e58b101f..af78130899e 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -1,6 +1,7 @@
 use std::assert_matches::assert_matches;
 
 use hir::Node;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -333,6 +334,19 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
         predicates.extend(const_evaluatable_predicates_of(tcx, def_id, &predicates));
     }
 
+    let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
+    // FIXME(staged_api): We might want to look at the normal stability attributes too but
+    // first we would need a way to let std/core use APIs with unstable feature bounds from
+    // within stable APIs.
+    let allow_unstable_feature_attr =
+        find_attr!(attrs, AttributeKind::UnstableFeatureBound(i) => i)
+            .map(|i| i.as_slice())
+            .unwrap_or_default();
+
+    for (feat_name, span) in allow_unstable_feature_attr {
+        predicates.insert((ty::ClauseKind::UnstableFeature(*feat_name).upcast(tcx), *span));
+    }
+
     let mut predicates: Vec<_> = predicates.into_iter().collect();
 
     // Subtle: before we store the predicates into the tcx, we
@@ -764,6 +778,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
                     ty::ClauseKind::RegionOutlives(_)
                     | ty::ClauseKind::ConstArgHasType(_, _)
                     | ty::ClauseKind::WellFormed(_)
+                    | ty::ClauseKind::UnstableFeature(_)
                     | ty::ClauseKind::ConstEvaluatable(_) => {
                         bug!(
                             "unexpected non-`Self` predicate when computing \
@@ -791,6 +806,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
                     | ty::ClauseKind::ConstArgHasType(_, _)
                     | ty::ClauseKind::WellFormed(_)
                     | ty::ClauseKind::ConstEvaluatable(_)
+                    | ty::ClauseKind::UnstableFeature(_)
                     | ty::ClauseKind::HostEffect(..) => {
                         bug!(
                             "unexpected non-`Self` predicate when computing \
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 574d19a5aa5..0043f0c7117 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
@@ -499,6 +499,7 @@ fn trait_specialization_kind<'tcx>(
         | ty::ClauseKind::ConstArgHasType(..)
         | ty::ClauseKind::WellFormed(_)
         | ty::ClauseKind::ConstEvaluatable(..)
+        | ty::ClauseKind::UnstableFeature(_)
         | ty::ClauseKind::HostEffect(..) => None,
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs
index 2c1d443f951..d3a57a4d8e5 100644
--- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs
@@ -54,6 +54,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
                     | ty::ClauseKind::ConstArgHasType(_, _)
                     | ty::ClauseKind::WellFormed(_)
                     | ty::ClauseKind::ConstEvaluatable(_)
+                    | ty::ClauseKind::UnstableFeature(_)
                     | ty::ClauseKind::HostEffect(..) => {}
                 }
             }