Skip to content

Commit

Permalink
redo the way we do const stability inheritance
Browse files Browse the repository at this point in the history
this lets us remove a bunch of rustc_const_stable_indirect attributes
  • Loading branch information
RalfJung committed Oct 7, 2024
1 parent 203ca72 commit d7a78db
Show file tree
Hide file tree
Showing 26 changed files with 163 additions and 156 deletions.
100 changes: 67 additions & 33 deletions compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::parse::feature_err;
use rustc_session::{RustcVersion, Session};
use rustc_span::Span;
use rustc_span::hygiene::Transparency;
use rustc_span::symbol::{Symbol, kw, sym};
use rustc_span::{DUMMY_SP, Span};

use crate::fluent_generated;
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
Expand Down Expand Up @@ -92,13 +92,15 @@ impl Stability {
#[derive(HashStable_Generic)]
pub struct ConstStability {
pub level: StabilityLevel,
/// This can be `None` for functions that are not even const-unstable, but
/// are tracked here for the purpose of `safe_to_expose_on_stable`.
/// Says whether this function has an explicit `rustc_const_(un)stable` attribute.
/// If `false`, the const stability information was inferred from the regular
/// stability information.
pub has_const_stable_attr: bool,
/// This can be `None` for functions that are not const-callable from outside code under any
/// feature gate, but are tracked here for the purpose of `safe_to_expose_on_stable`.
pub feature: Option<Symbol>,
/// A function that is marked as "safe to expose on stable" must not use any unstable const
/// language features or intrinsics, and all the functions it calls must also be safe to expose
/// on stable. If `level` is `Stable`, this must be `true`.
pub safe_to_expose_on_stable: bool,
/// This is true iff the `const_stable_indirect` attribute is present.
pub const_stable_indirect: bool,
/// whether the function has a `#[rustc_promotable]` attribute
pub promotable: bool,
}
Expand Down Expand Up @@ -275,22 +277,21 @@ pub fn find_stability(
/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
/// attributes in `attrs`. Returns `None` if no stability attributes are found.
///
/// `inherited_feature_gate` says which feature gate this function should be under if it doesn't
/// declare a gate itself, but has `#[rustc_const_stable_indirect]`.
/// The second component is the `const_stable_indirect` attribute if present, which can be meaningful
/// even if there is no `rustc_const_stable`/`rustc_const_unstable`.
pub fn find_const_stability(
sess: &Session,
attrs: &[Attribute],
item_sp: Span,
inherited_feature_gate: Option<Symbol>,
) -> Option<(ConstStability, Span)> {
) -> (Option<(ConstStability, Span)>, Option<Span>) {
let mut const_stab: Option<(ConstStability, Span)> = None;
let mut promotable = false;
let mut const_stable_indirect = false;
let mut const_stable_indirect = None;

for attr in attrs {
match attr.name_or_empty() {
sym::rustc_promotable => promotable = true,
sym::rustc_const_stable_indirect => const_stable_indirect = true,
sym::rustc_const_stable_indirect => const_stable_indirect = Some(attr.span),
sym::rustc_const_unstable => {
if const_stab.is_some() {
sess.dcx()
Expand All @@ -302,8 +303,9 @@ pub fn find_const_stability(
const_stab = Some((
ConstStability {
level,
has_const_stable_attr: true,
feature: Some(feature),
safe_to_expose_on_stable: false,
const_stable_indirect: false,
promotable: false,
},
attr.span,
Expand All @@ -320,8 +322,9 @@ pub fn find_const_stability(
const_stab = Some((
ConstStability {
level,
has_const_stable_attr: true,
feature: Some(feature),
safe_to_expose_on_stable: true,
const_stable_indirect: false,
promotable: false,
},
attr.span,
Expand All @@ -343,38 +346,69 @@ pub fn find_const_stability(
}
}
}
if const_stable_indirect {
if const_stable_indirect.is_some() {
match &mut const_stab {
Some((stab, _)) => {
if stab.is_const_unstable() {
stab.safe_to_expose_on_stable = true;
stab.const_stable_indirect = true;
} else {
_ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing {
span: item_sp,
})
}
}
_ => {
// `#[rustc_const_stable_indirect]` implicitly makes the function unstably const,
// inheriting the feature gate from `#[unstable]` if it xists, or without any
// feature gate otherwise.
let c = ConstStability {
feature: inherited_feature_gate,
safe_to_expose_on_stable: true,
promotable: false,
level: StabilityLevel::Unstable {
reason: UnstableReason::Default,
issue: None,
is_soft: false,
implied_by: None,
},
};
const_stab = Some((c, DUMMY_SP));
// We ignore the `#[rustc_const_stable_indirect]` here, it should be picked up by
// the `default_const_unstable` logic.
}
}
}

const_stab
(const_stab, const_stable_indirect)
}

/// Called for `fn` that don't have a const stability.
///
/// `effective_reg_stability` must be the effecive regular stability, i.e. after applying all the
/// rules about "inherited" stability.
pub fn default_const_stability(
_sess: &Session,
is_const_fn: bool,
const_stable_indirect: bool,
effective_reg_stability: Option<&Stability>,
) -> Option<ConstStability> {
// Intrinsics are *not* `const fn` here, and yet we want to add a default const stability
// for them if they are marked `const_stable_indirect`.
if (is_const_fn || const_stable_indirect)
&& let Some(reg_stability) = effective_reg_stability
&& reg_stability.level.is_unstable()
{
// This has a feature gate, reuse that for const stability.
// We only want to do this if it is an unstable feature gate.
Some(ConstStability {
feature: Some(reg_stability.feature),
has_const_stable_attr: false,
const_stable_indirect,
promotable: false,
level: reg_stability.level,
})
} else if const_stable_indirect {
// Make it const-unstable without a feature gate, to record the `const_stable_indirect`.
Some(ConstStability {
feature: None,
has_const_stable_attr: false,
const_stable_indirect,
promotable: false,
level: StabilityLevel::Unstable {
reason: UnstableReason::Default,
issue: None,
is_soft: false,
implied_by: None,
},
})
} else {
None
}
}

/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`.
Expand Down
21 changes: 12 additions & 9 deletions compiler/rustc_const_eval/src/check_consts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,21 @@ pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> b
// Intrinsics default to "not exposed on stable".
return false;
}
// We allow calling unmarked local functions *in the current crate*. For the cross-crate
// case we require the other crate to explicitly add `#[rustc_const_stable_indirect]` as
// a promise that this function is meant to be indirectly const-stable, which will make
// `lookup_const_stability` return `Some`.
// We allow calling unmarked local functions *in the current crate*. This covers in
// particular all `const fn` whose inherited regular stability is `stable`, which means
// we don't infer any const stability for them. For the cross-crate case we require the
// other crate to explicitly add `#[rustc_const_stable_indirect]` as a promise that this
// function is meant to be indirectly const-stable, which will make
// `lookup_const_stability` return `Some`. This ensures that the other crate checked
// recursive const vailidty on that function, even if the other crate is not using
// `staged_api`.
def_id.is_local()
}
Some(stab) => {
// `safe_to_expose_on_stable` implies `is_const_stable`.
if stab.is_const_stable() {
assert!(stab.safe_to_expose_on_stable);
}
stab.safe_to_expose_on_stable
stab.is_const_stable() || stab.const_stable_indirect ||
// Non-intrinsic `const fn` without an explicit const stability attribute (i.e.,
// with only the implied attribute) are safe to expose on stable.
(!stab.has_const_stable_attr && tcx.intrinsic(def_id).is_none())
}
}
}
13 changes: 8 additions & 5 deletions compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -865,18 +865,21 @@ impl SyntaxExtension {
})
.unwrap_or_else(|| (None, helper_attrs));
let stability = attr::find_stability(sess, attrs, span);
// FIXME: this will give a different result than the normal stability computation, since we
// don't inherit stability from the parent. But that's true even for `stability` above so
// it's probably okay?
let const_stability =
attr::find_const_stability(sess, attrs, span, stability.map(|(s, _)| s.feature));
let (const_stability, const_stable_indirect) =
attr::find_const_stability(sess, attrs, span);
let body_stability = attr::find_body_stability(sess, attrs);
if let Some((_, sp)) = const_stability {
sess.dcx().emit_err(errors::MacroConstStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),
});
}
if let Some(sp) = const_stable_indirect {
sess.dcx().emit_err(errors::MacroConstStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),
});
}
if let Some((_, sp)) = body_stability {
sess.dcx().emit_err(errors::MacroBodyStability {
span: sp,
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,6 @@ passes_maybe_string_interpolation = you might have meant to use string interpola
passes_missing_const_err =
attributes `#[rustc_const_unstable]`, `#[rustc_const_stable]` and `#[rustc_const_stable_indirect]` require the function or method to be `const`
.help = make the function or method const
.label = attribute specified here
passes_missing_const_stab_attr =
{$descr} has missing const stability attribute
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1551,8 +1551,6 @@ pub(crate) struct MissingConstErr {
#[primary_span]
#[help]
pub fn_sig_span: Span,
#[label]
pub const_span: Span,
}

#[derive(Diagnostic)]
Expand Down
Loading

0 comments on commit d7a78db

Please sign in to comment.